Java单继承限制下Android自定义View拆分Arc类的实现方法咨询
解决Android自定义View多圆弧的代码冗余问题(避开Java单继承限制)
嗨,这个问题太常见啦!Java的单继承规则确实会让我们在想复用View相关逻辑的时候有点头疼,但咱们完全可以用**组合(Composition)**替代继承来搞定——简单说就是把单个圆弧的绘制逻辑封装成一个独立的普通类,然后在自定义View里把这些类组合起来用,完美避开继承限制,还能减少代码冗余!
核心思路
不用让你的圆弧类继承View(毕竟自定义View已经占了唯一的继承名额),而是创建一个专门负责绘制单个圆弧逻辑的普通Java类(比如叫ArcRenderer),把圆弧的属性(颜色、半径、角度等)和绘制方法都放在这个类里。然后你的自定义View(比如TripleArcView)只需要继承View,并维护三个ArcRenderer实例,在onDraw里依次调用它们的绘制方法就行。
代码示例
1. 封装单个圆弧的绘制类 ArcRenderer
这个类不用继承任何View相关类,只专注于圆弧的绘制逻辑:
public class ArcRenderer { // 圆弧的核心属性 private float radius; private float startAngle; private float sweepAngle; private Paint arcPaint; private RectF arcBounds; // 圆弧的外接矩形,控制位置 // 构造方法:初始化圆弧的基础属性 public ArcRenderer(int arcColor, float radius, float startAngle, float sweepAngle) { this.radius = radius; this.startAngle = startAngle; this.sweepAngle = sweepAngle; // 初始化画笔,开启抗锯齿 arcPaint = new Paint(Paint.ANTI_ALIAS_FLAG); arcPaint.setColor(arcColor); arcPaint.setStyle(Paint.Style.STROKE); arcPaint.setStrokeWidth(8f); // 圆弧的线条宽度,可按需调整 } // 设置圆弧的位置(外接矩形) public void setBounds(float left, float top, float right, float bottom) { if (arcBounds == null) { arcBounds = new RectF(); } arcBounds.set(left, top, right, bottom); } // 对外提供绘制方法 public void draw(Canvas canvas) { if (arcBounds == null) return; canvas.drawArc(arcBounds, startAngle, sweepAngle, false, arcPaint); } // 可选:添加getter/setter,支持动态修改圆弧属性 public void setArcColor(int color) { arcPaint.setColor(color); } }
2. 自定义View TripleArcView
这个类继承View,负责布局三个圆弧并协调绘制:
public class TripleArcView extends View { private ArcRenderer arc1; private ArcRenderer arc2; private ArcRenderer arc3; private float arcSpacing; // 圆弧之间的间距 // 构造方法,覆盖所有View的构造场景 public TripleArcView(Context context) { super(context); init(); } public TripleArcView(Context context, AttributeSet attrs) { super(context, attrs); init(); } public TripleArcView(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); init(); } // 初始化三个圆弧实例 private void init() { arcSpacing = dpToPx(16); // 把dp转成px,适配不同屏幕 // 根据需求初始化三个圆弧的颜色、半径、角度 arc1 = new ArcRenderer(Color.RED, dpToPx(40), 180, 180); arc2 = new ArcRenderer(Color.GREEN, dpToPx(40), 180, 180); arc3 = new ArcRenderer(Color.BLUE, dpToPx(40), 180, 180); } // 计算View的尺寸,确保三个圆弧能水平放下 @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { float arcDiameter = dpToPx(80); // 半径40,直径就是80 // 计算期望宽度:3个圆弧直径 + 2个间距 + 左右内边距 int desiredWidth = (int) (arcDiameter * 3 + arcSpacing * 2 + getPaddingLeft() + getPaddingRight()); // 计算期望高度:圆弧直径 + 上下内边距 int desiredHeight = (int) (arcDiameter + getPaddingTop() + getPaddingBottom()); // 解析系统测量规格,确定最终尺寸 int finalWidth = resolveSize(desiredWidth, widthMeasureSpec); int finalHeight = resolveSize(desiredHeight, heightMeasureSpec); setMeasuredDimension(finalWidth, finalHeight); } // 绘制三个圆弧 @Override protected void onDraw(Canvas canvas) { super.onDraw(canvas); float arcDiameter = dpToPx(80); float currentX = getPaddingLeft(); // 绘制第一个圆弧 arc1.setBounds(currentX, getPaddingTop(), currentX + arcDiameter, getPaddingTop() + arcDiameter); arc1.draw(canvas); // 移动到第二个圆弧的位置(加上间距) currentX += arcDiameter + arcSpacing; arc2.setBounds(currentX, getPaddingTop(), currentX + arcDiameter, getPaddingTop() + arcDiameter); arc2.draw(canvas); // 移动到第三个圆弧的位置 currentX += arcDiameter + arcSpacing; arc3.setBounds(currentX, getPaddingTop(), currentX + arcDiameter, getPaddingTop() + arcDiameter); arc3.draw(canvas); } // 工具方法:dp转px private float dpToPx(float dp) { return TypedValue.applyDimension( TypedValue.COMPLEX_UNIT_DIP, dp, getResources().getDisplayMetrics() ); } }
为什么这个方案好用?
- 避开单继承限制:
ArcRenderer是普通Java类,不需要继承任何View相关类,完全不影响自定义View继承View - 代码解耦&复用:单个圆弧的绘制逻辑都在
ArcRenderer里,修改圆弧样式只需要改这个类;以后其他View需要圆弧,直接复用这个类就行 - 职责清晰:自定义View只负责布局和协调多个圆弧,
ArcRenderer只负责单个圆弧的绘制,符合单一职责原则 - 扩展性强:如果以后要加第四个、第五个圆弧,只需要在自定义View里多实例化一个
ArcRenderer就行,改动极小
额外补充:如果需要处理触摸事件
要是你的圆弧需要响应点击、触摸,也可以把触摸判断逻辑封装到ArcRenderer里,比如给ArcRenderer加一个isTouchInArc(float x, float y)方法,然后在自定义View的onTouchEvent里遍历三个圆弧,判断触摸点落在哪个圆弧上,再做相应处理。
内容的提问来源于stack exchange,提问作者B.Cakir
相关产品推荐
相关产品推荐

