【踩坑记录】Android自定义控件——流程进度条

本文记录了一位开发者在实习期间实现一个自定义Android进度条控件的过程,包括设计分析、代码实现、遇到的测量问题及解决方案。在实现过程中,通过分析布局和测量模式,解决了进度条高度自适应内容高度的问题,同时适配了滑动布局。文章还分享了实际开发中的一些趣事和学习心得。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

前言

实习期间被分配到的第一个任务,完成大概如图这样一个界面。乍一看,整个界面的布局还是十分清晰的,即使是新手也能轻易完成。唯一的难题应该就是这个红色的进度条了,我一开始考虑使用TextView的drawableLeft来实现,但又感觉不如自定义控件来得灵活,遂决定使用自定义控件的方式实现。然而,我高估了自己的水平😅,过程中遇到了不少坑,花了几天才误打误撞地完成这个“简易”进度条,也正因如此,才有了这篇文章来记录一下思考过程、遇到的问题以及解决方案。

设计图

设计分析

实现这样一个控件,要考虑的方面有:绘制(Draw)、测量(Measure)以及属性(Attribute)。

  • 绘制。显而易见,绘制一条直线和几个圆形即可。
  • 测量。我选择以圆形的直径为控件的宽(width),父容器的高度为控件的高(height)。
  • 属性。比较自由,如直线的粗细、圆形的半径、圆环的位置等。为简便起见,只考虑几个比较关键的属性。

实现

根据上述分析,代码的逻辑也基本理清了,实现起来应该是水到渠成。

创建类

新建MyProgressView类,继承自View类,并实现相关构造方法,使代码入口一致。

public class MyProgressView extends View {
   

    private static final String TAG = "MyProgressView";

    public MyProgressView(Context context) {
   
        this(context, null);
    }

    public MyProgressView(Context context, @Nullable AttributeSet attrs) {
   
        this(context, attrs, 0);
    }

    public MyProgressView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
   
        super(context, attrs, defStyleAttr);
    }

设置属性

为方便使用,部分属性希望能在布局文件中直接修改,将其添加至attrs.xml文件中,并在控件的构造方法中完成初始化。另一部分属性,可能不方便于布局文件中修改或其他原因,为其设置get/set方法。

    <declare-styleable name="MyProgressView">
        <attr name="circleRadius" format="dimension"/>
        <attr name="lineWidth" format="dimension"/>
        <attr name="circlePosition1" format="float"/>
        <attr name="circlePosition2" format="float"/>
        <attr name="circlePosition3" format="float"/>
    </declare-styleable>
public class MyProgressView extends View {
   

    private static final String TAG = "MyProgressView";
	
	//控件的高度
    private int mHeight;
	
    //绘制的起始点
    private float mStartY = DEFAULT_START_Y;
    public static final float DEFAULT_START_Y = 0;

    //实线的宽度
    private float mLineWidth;

    //圆环的半径
    private float mRadius;

    //圆环的位置(圆心)
    private float[] mCirclePositions = new float[3];

    //当前进程
    private int mProgress = -1;

    public MyProgressView(Context context) {
   
        this(context, null);
    }

    public MyProgressView(Context context, @Nullable AttributeSet attrs) {
   
        this(context, attrs, 0);
    }

    public MyProgressView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
   
        super(context, attrs, defStyleAttr);
        //初始化属性
        initAttrs(context, attrs);
    }

    private void initAttrs(Context context, AttributeSet attrs) {
   
        TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.MyProgressView);
        //圆环的半径
        int defaultRadius = SizeUtils.dip2px(5);
        mRadius = a.getDimension(R.styleable.MyProgressView_circleRadius, defaultRadius);
        //实线的宽度
        int defaultWidth = SizeUtils.dip2px(2);
        mLineWidth = a.getDimension(R.styleable.MyProgressView_lineWidth, defaultWidth);
        /*
         * 三个圆环的位置(y轴)
         * 推荐使用setCirclePositions方法设置
         */
        mCirclePositions[0] = a.getFloat(R.styleable.MyProgressView_circlePosition1, -1);
        mCirclePositions[1] = a.getFloat(R.styleable.MyProgressView_circlePosition2, -1);
        mCirclePositions[2] = a.getFloat(R.styleable.MyProgressView_circlePosition3, -1);
        //回收
        a.recycle();
    }

	/*
     * 省略部分属性的get/set方法
     */
}

绘制

绘制直线和圆形所需要的的相关属性已经定义好了,除此之外还需要实例化对应的画笔。

    private void initPaints() {
   
        //初始化实线画笔
        mLinePaint = new Paint();
        mLinePaint.setColor(Color.parseColor("#FF0000"));
        mLinePaint.setStrokeWidth(mLineWidth);
        //初始化圆形画笔
        innerCirclePaint = new Paint();
        outerCirclePaint = new Paint();
        innerCirclePaint.setColor(Color.parseColor("#FFFFFF"));
        outerCirclePaint.setColor(Color.parseColor("#D0021B"));
    }

紧接着就是绘制过程。圆环利用两个重合的大小圆形实现,根据mProgress的值决定是否只绘制一个圆形,以表示当前流程。

	@Override
    protected void onDraw(Canvas canvas) {
   
        Log.i(TAG,"-- onDraw --");
        super.onDraw(canvas);
        //绘制实线
        canvas.drawLine(mRadius, mStartY, mRadius, mHeight, mLinePaint);
        //绘制圆环
        if (mCirclePositions != null && mCirclePositions.length > 0) {
   
            for (int i = 1; i <= 3; i ++) {
   
                float f = mCirclePositions[i - 1];
                if (f == -1) 
评论 8
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值