如何为RadioButton实现可动态修改文本的自定义按钮?
我来给你几个实用的解决思路,亲测可行,你可以根据自己的需求选:
这是最灵活的方式,完全自己控制绘制逻辑,想怎么画就怎么画:
- 先创建一个自定义类继承RadioButton,加个成员变量存要显示的圆形内文本,再写个setter方法用来动态改文本,改完记得调用
invalidate()触发重绘。 - 重写
onDraw方法,先处理RadioButton本身的文本绘制(如果不需要可以跳过),然后计算圆形边框的位置,根据选中状态画不同样式的边框(比如选中时边框粗一点、颜色深一点),最后在圆形中间画文本,注意居中对齐。
给你个简化版的代码示例:
public class CustomTextRadioButton extends RadioButton { private String mCircleText = "A"; private Paint mCirclePaint; private Paint mTextPaint; private int mCircleRadius = dp2px(20); public CustomTextRadioButton(Context context) { super(context); init(); } // 别忘了实现带AttributeSet和Theme的构造函数,方便布局里使用 public CustomTextRadioButton(Context context, AttributeSet attrs) { super(context, attrs); init(); } public CustomTextRadioButton(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); init(); } private void init() { mCirclePaint = new Paint(Paint.ANTI_ALIAS_FLAG); mTextPaint = new Paint(Paint.ANTI_ALIAS_FLAG); mTextPaint.setTextSize(sp2px(16)); mTextPaint.setTextAlign(Paint.Align.CENTER); } public void setCircleText(String text) { mCircleText = text; invalidate(); } @Override protected void onDraw(Canvas canvas) { // 先画RadioButton默认的文本 super.onDraw(canvas); // 计算圆形位置,放在控件左侧和默认button位置一致 int circleX = getPaddingLeft() + mCircleRadius; int circleY = getHeight() / 2; // 根据选中状态设置样式 if (isChecked()) { mCirclePaint.setColor(getResources().getColor(R.color.radio_selected)); mCirclePaint.setStyle(Paint.Style.STROKE); mCirclePaint.setStrokeWidth(dp2px(2)); mTextPaint.setColor(getResources().getColor(R.color.radio_selected)); } else { mCirclePaint.setColor(getResources().getColor(R.color.radio_unselected)); mCirclePaint.setStyle(Paint.Style.STROKE); mCirclePaint.setStrokeWidth(dp2px(1)); mTextPaint.setColor(getResources().getColor(R.color.radio_unselected)); } // 画圆形边框 canvas.drawCircle(circleX, circleY, mCircleRadius - dp2px(1), mCirclePaint); // 画居中文本 Paint.FontMetrics metrics = mTextPaint.getFontMetrics(); float textY = circleY + (metrics.bottom - metrics.top) / 2 - metrics.bottom; canvas.drawText(mCircleText, circleX, textY, mTextPaint); } // dp/sp转px的工具方法 private int dp2px(int dp) { return (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, dp, getResources().getDisplayMetrics()); } private int sp2px(int sp) { return (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_SP, sp, getResources().getDisplayMetrics()); } }
布局里直接用这个自定义控件,需要改文本的时候调用setCircleText("B")就行。
方案二:用LayerDrawable + 自定义TextDrawable
不想自定义View的话,可以用Drawable组合来实现:
- 先写一个
TextDrawable,继承自Drawable,支持动态设置文本,重写draw方法绘制文本。 - 然后把圆形边框的shape drawable和这个TextDrawable组合成LayerDrawable,再给RadioButton设置成button drawable。如果要处理选中状态,就用StateListDrawable,分别给选中和未选中状态配置对应的LayerDrawable。
- 动态改文本时,从RadioButton的button drawable里拿到TextDrawable,调用setText方法就好。
简化版TextDrawable代码:
public class TextDrawable extends Drawable { private String mText; private Paint mPaint; public TextDrawable(String text) { mText = text; mPaint = new Paint(Paint.ANTI_ALIAS_FLAG); mPaint.setTextAlign(Paint.Align.CENTER); mPaint.setTextSize(sp2px(16)); } public void setText(String text) { mText = text; invalidateSelf(); } @Override public void draw(Canvas canvas) { Rect bounds = getBounds(); float x = bounds.centerX(); float y = bounds.centerY() - (mPaint.descent() + mPaint.ascent()) / 2; canvas.drawText(mText, x, y, mPaint); } @Override public void setAlpha(int alpha) { mPaint.setAlpha(alpha); } @Override public void setColorFilter(ColorFilter colorFilter) { mPaint.setColorFilter(colorFilter); } @Override public int getOpacity() { return PixelFormat.TRANSLUCENT; } private int sp2px(int sp) { return (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_SP, sp, getResources().getDisplayMetrics()); } }
方案三:布局叠加(最简单快捷)
如果不想写任何自定义代码,用布局组合就能快速搞定:
- 用ConstraintLayout或者RelativeLayout把RadioButton和TextView叠在一起,先给RadioButton设置
android:button="@null"隐藏默认按钮。 - 给TextView设置圆形边框的shape背景,把它定位在RadioButton原来button的位置(比如左侧)。
- 给RadioButton加选中状态监听,选中时改变TextView的边框和文本颜色,未选中时恢复。TextView的文本直接用
setText()动态修改就行。
布局示例:
<androidx.constraintlayout.widget.ConstraintLayout android:layout_width="wrap_content" android:layout_height="wrap_content"> <RadioButton android:id="@+id/radio_option" android:layout_width="wrap_content" android:layout_height="wrap_content" android:button="@null" android:paddingLeft="48dp" android:text="你的选项文本"/> <TextView android:id="@+id/circle_text" android:layout_width="40dp" android:layout_height="40dp" android:layout_marginEnd="16dp" android:background="@drawable/circle_border_normal" android:gravity="center" android:text="A" app:layout_constraintBottom_toBottomOf="@id/radio_option" app:layout_constraintLeft_toLeftOf="parent" app:layout_constraintTop_toTopOf="@id/radio_option"/> </androidx.constraintlayout.widget.ConstraintLayout>
代码里的选中监听:
RadioButton radioBtn = findViewById(R.id.radio_option); TextView circleTv = findViewById(R.id.circle_text); radioBtn.setOnCheckedChangeListener((buttonView, isChecked) -> { if (isChecked) { circleTv.setBackgroundResource(R.drawable.circle_border_selected); circleTv.setTextColor(getResources().getColor(R.color.selected_color)); } else { circleTv.setBackgroundResource(R.drawable.circle_border_normal); circleTv.setTextColor(getResources().getColor(R.color.unselected_color)); } }); // 动态修改文本 circleTv.setText("B");
这个方案零自定义代码,上手最快,适合快速实现需求。
内容的提问来源于stack exchange,提问作者Algar
相关产品推荐
相关产品推荐

