自定义控件之动态进度View

本文介绍了一个自定义的加载动画视图实现方法,详细解释了如何使用Android Canvas和Path API来逐步绘制出复杂的动画效果,包括箭头渐变、弹出圆球等。

效果

首先看一下效果
这里写图片描述

实现思路

仔细看这个效果,将整个过程一个一个的分解:

分解
    1.画一个箭头
    2.箭头渐变
    3.弹出圆球
    4.画圆弧
    5.画勾
每个过程通过一个进度百分比来记录进度,然后就是按顺序一个个的画出来
public class LoadingView extends View {

    // 开始绘制的标志
    private boolean mCanDraw = true;
    private boolean mIsStart = true;
    private int mDrawUpLinePercent;
    private int mDrawOnLinePercent;
    private int mOutPointPercent;
    private int mDrawGouPercent;

    private int mWidth = 0;
    private int mheight = 0;
    // 圆心半径
    private float mCircleX = 0;
    private float mCircleY = 0;
    private float mRedius = 80;
    private int mOuterCircleStrokeWidth = 10;
    private Paint mPaint;
    private RectF mRectF;

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

    public LoadingView(Context context, AttributeSet attrs) {
        super(context, attrs);

        mPaint = new Paint();
        mPaint.setAntiAlias(true);
        mPaint.setStrokeWidth(mOuterCircleStrokeWidth);
        mPaint.setStyle(Paint.Style.STROKE);
        mPaint.setColor(Color.parseColor("#44FFFFFF"));

    }

    @Override
    protected void onDraw(Canvas canvas) {

        // 获取圆心
        mCircleX = mWidth / 2;
        mCircleY = mheight / 2;
        // 这个path一定只能在这里初始化
        Path path = new Path();

        mRectF = new RectF(mCircleX - mRedius, mCircleY - mRedius, mCircleX + mRedius, mCircleY + mRedius);


        // 绘制外圆
        mPaint.setColor(Color.parseColor("#44FFFFFF"));
        canvas.drawCircle(mCircleX, mCircleX, mRedius, mPaint);

        if (mCanDraw) {

            mIsStart = true;
            mPaint.setColor(Color.WHITE);
            if (mDrawUpLinePercent < 95) {

                // 绘制竖线
                float temp = (mCircleY / 4) * mDrawUpLinePercent / 100;
                canvas.drawLine(mCircleX, mCircleY * 3 / 4 + temp,
                        mCircleX, mCircleY * 5 / 4 - temp, mPaint);

                // 竖线变化的时候 箭头一直存在
                path.moveTo(mCircleX * 3 / 4, mCircleY);
                path.lineTo(mCircleX, mCircleY * 5 / 4);
                path.lineTo(mCircleX * 5 / 4, mCircleY);
                canvas.drawPath(path, mPaint);
                mDrawUpLinePercent += 5;
            } else {
                // path变成直线
                if (mDrawOnLinePercent < 100) {

                    // 箭头变形
                    path.moveTo(mCircleX * 3 / 4, mCircleY);
                    float temp = (mCircleY / 4) * mDrawOnLinePercent / 100;
                    path.lineTo(mCircleX, mCircleY * 5 / 4 - temp);
                    path.lineTo(mCircleX * 5 / 4, mCircleY);
                    canvas.drawPath(path, mPaint);
                    mDrawOnLinePercent += 5;

                    // 圆心一直存在
                    canvas.drawCircle(mCircleX, mCircleY, 2.5f, mPaint);
                } else {

                    // 将点弹出圆外
                    if (mOutPointPercent < 100) {

                        float temp = mRedius * mOutPointPercent / 100;
                        canvas.drawCircle(mCircleX, mCircleY - temp, 2.5f, mPaint);
                        mOutPointPercent += 5;
                        //弹出圆外的过程中,直线是一直存在的
                        canvas.drawLine(mCircleX * 3 / 4, mCircleY, mCircleX * 5 / 4, mCircleY, mPaint);

                        mOutPointPercent += 5;
                    } else {

                        if (mDrawGouPercent < 100) {

                            // 同时画勾
                            path.moveTo(mCircleX * 3 / 4, mCircleY);
                            float t = (mCircleY / 4) * mDrawGouPercent / 100;
                            path.lineTo(mCircleX, mCircleY + t);
                            path.lineTo(mCircleX * 5 / 4, mCircleY);
                            canvas.drawPath(path, mPaint);

                            // 画圆弧
                            mPaint.setStrokeWidth(mOuterCircleStrokeWidth);
                            float temp = (mDrawGouPercent / 100f) * 360;
                            canvas.drawArc(mRectF, 270, -temp, false, mPaint);

                            mDrawGouPercent += 5;
                        } else {

                            // 绘制最终的圆
                            mPaint.setColor(Color.WHITE);
                            canvas.drawCircle(mCircleX, mCircleY, mRedius, mPaint);
                            // 绘制最终的勾
                            path.moveTo(mCircleX * 3 / 4, mCircleY);
                            path.lineTo(mCircleX, mCircleY * 5 / 4);
                            path.lineTo(mCircleX * 5 / 4, mCircleY * 4 / 5);
                            canvas.drawPath(path, mPaint);

                            // 绘制完成
                            mCanDraw = false;
                        }
                    }
                }
            }
        } else {


            // 绘制最终的圆
            mPaint.setColor(Color.WHITE);
            canvas.drawCircle(mCircleX, mCircleY, mRedius, mPaint);
            // 绘制最终的勾
            path.moveTo(mCircleX * 3 / 4, mCircleY);
            path.lineTo(mCircleX, mCircleY * 5 / 4);
            path.lineTo(mCircleX * 5 / 4, mCircleY * 4 / 5);
            canvas.drawPath(path, mPaint);
            mIsStart = false;
            // 绘制静态箭头
//            canvas.drawLine(mCircleX, mCircleY * 3 / 4,
//                    mCircleX, mCircleY * 5 / 4, mPaint);
//
//            path.moveTo(mCircleX * 3 / 4, mCircleY);
//            path.lineTo(mCircleX, mCircleY * 5 / 4);
//            path.lineTo(mCircleX * 5 / 4, mCircleY);
//            canvas.drawPath(path, mPaint);
        }
        if (mIsStart) {

            postInvalidateDelayed(30);
        }
    }


    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {

        int widthMode = MeasureSpec.getMode(widthMeasureSpec);
        int heightMode = MeasureSpec.getMode(heightMeasureSpec);
        int width = MeasureSpec.getSize(widthMeasureSpec);
        int height = MeasureSpec.getSize(heightMeasureSpec);

        if (widthMode == MeasureSpec.EXACTLY) {

            mWidth = width;
        } else {

            mWidth = 400;
        }
        if (heightMode == MeasureSpec.EXACTLY) {

            mheight = height;
        } else {

            mheight = 400;
        }

        // 设置自己的真实大小
        setMeasuredDimension(width, height);
    }

}

踩的坑

这个view我自己调了一下午才调出来,还几次都想放弃,最后还是坚持下来了,给自己点个赞!下面说说我遇到的问题吧:

1.坐标计算不熟悉,花费了很多时间,动手比较少
2.这个进度onDraw每次触发的时候,我们希望重新绘制下个画面,然而我在画直线的时候将new Path()放在了全局,相当于每次对这个path对象操作,导致每次onDraw画的直线都在,花了好久才找到原因!
3.Paint的一些属性改变之后需要重新设置.
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值