如何通过Drawable实现票证视图右侧固定宽度不拉伸、左侧自适应?
当然可以用Drawable实现这个需求!
这种方案比双布局加双背景的权宜之计更优雅,还能减少视图层级嵌套,让代码结构更简洁。下面给你两种可行的实现方式:
方案一:使用NinePatchDrawable(最快捷)
NinePatch是Android原生支持的可拉伸Drawable类型,正好能满足你“指定部分不拉伸”的需求:
- 制作NinePatch图:用Android Studio自带的NinePatch工具打开你的票证背景原图,在右侧QR区域的左侧边缘画一条垂直的黑色标记线(标记为不可拉伸区域),在左侧区域的中间画一条垂直标记线(标记为可拉伸区域)。顶部的标记线可以根据需要设置垂直方向的拉伸规则,底部标记线用来定义内容的padding范围。
- 布局配置:用一个
LinearLayout或ConstraintLayout作为容器,将其背景设置为这个NinePatchDrawable。然后在容器内添加两个子View:- 左侧View:在
LinearLayout中设置layout_weight="1"+layout_width="0dp",或者在ConstraintLayout中设置layout_width="0dp"并绑定左右约束,让它自动填充剩余空间。 - 右侧View:设置固定宽度(比如
android:width="120dp"),用来放置QR码组件。
- 左侧View:在
方案二:自定义Drawable(更灵活)
如果你的票证背景有复杂的样式,NinePatch满足不了,完全可以自定义Drawable来精确控制拉伸逻辑:
- 编写自定义Drawable类:
public class TicketBackgroundDrawable extends Drawable { private final Drawable leftStretchableBg; private final Drawable rightFixedBg; private final int fixedRightWidth; public TicketBackgroundDrawable(Drawable leftBg, Drawable rightBg, int rightWidth) { this.leftStretchableBg = leftBg; this.rightFixedBg = rightBg; this.fixedRightWidth = rightWidth; } @Override public void draw(@NonNull Canvas canvas) { Rect bounds = getBounds(); // 绘制左侧可拉伸区域:宽度为总宽度减去右侧固定宽度 leftStretchableBg.setBounds(bounds.left, bounds.top, bounds.right - fixedRightWidth, bounds.bottom); leftStretchableBg.draw(canvas); // 绘制右侧固定宽度区域 rightFixedBg.setBounds(bounds.right - fixedRightWidth, bounds.top, bounds.right, bounds.bottom); rightFixedBg.draw(canvas); } @Override public void setAlpha(int alpha) { leftStretchableBg.setAlpha(alpha); rightFixedBg.setAlpha(alpha); } @Override public void setColorFilter(@Nullable ColorFilter colorFilter) { leftStretchableBg.setColorFilter(colorFilter); rightFixedBg.setColorFilter(colorFilter); } @Override public int getOpacity() { return PixelFormat.TRANSLUCENT; } @Override protected void onBoundsChange(Rect bounds) { super.onBoundsChange(bounds); // 边界变化时重新更新两个子Drawable的绘制范围 leftStretchableBg.setBounds(bounds.left, bounds.top, bounds.right - fixedRightWidth, bounds.bottom); rightFixedBg.setBounds(bounds.right - fixedRightWidth, bounds.top, bounds.right, bounds.bottom); } }
- 在代码中实例化并设置:
// 获取左侧可拉伸背景、右侧固定背景,以及预设的右侧宽度 Drawable leftBg = getResources().getDrawable(R.drawable.ticket_left_bg, getTheme()); Drawable rightBg = getResources().getDrawable(R.drawable.ticket_right_bg, getTheme()); int rightAreaWidth = getResources().getDimensionPixelSize(R.dimen.qr_area_width); // 创建自定义Drawable并设置给容器视图 TicketBackgroundDrawable ticketBg = new TicketBackgroundDrawable(leftBg, rightBg, rightAreaWidth); findViewById(R.id.ticket_container).setBackground(ticketBg);
- 布局配置和方案一完全一致,左侧View自适应空间,右侧View固定宽度。
这两种方案都能完美实现你的需求,不用依赖双布局的权宜方案,推荐优先尝试NinePatch,开发成本更低;如果有复杂的动态样式需求,再用自定义Drawable。
内容的提问来源于stack exchange,提问作者Дмитро Яковлєв
相关产品推荐
相关产品推荐

