安卓开发之使用PathMeasure自定义加载动画控件


先上效果图:

这里写图片描述

一、PathMeasure

在绘制画布时,我们通常会使用Path对象来勾勒出绘制的路径,而PathMeasure可以让我们得到路径上的一些信息,比如获取路径上点的坐标,截取路径上的某一段小路径等。。

它主要有既个方法:

  • setPath(Path path, boolean forceClosed);

    与一个Path对象绑定,forceClose为true时,不管关联的Path是否是闭合的,都会被闭合。

  • getLength()

    获取路径的长度

  • getPosTan(float distance, float pos[],float tan[])

    distance 为一个 0 - getLength() 之间的值,根据这个距离值计算出当前点的xy坐标和正切值封装到 pos和tan当中;

  • boolean getSegment (float startD, float stopD, Path dst, boolean startWithMoveTo)

    判断截取路径成功时返回true;

    startD为开始截取位置距离 Path 起点的长度
    stopD 结束截取位置距离 Path 起点的长度
    取值范围: 0 <= startD < stopD <= Path总长度getLength(),超过范围截取不成功

    截取的 Path 将会添加到 dst 中

    startWithMoveTo 起始点是否使用 moveTo,用于保证截取的 Path 第一个点位置不变(一般为true)

    在默认开启硬件加速的情况下,更改 dst 的内容后可能绘制会出现问题,可以给 dst 添加一个操作:dst.lineTo(0, 0)

二、自定义动画控件

大概思路就是:

创建一个勾勒空心圆的Path对象
再根据上面的Path对象创建PathMeasure对象
最后再通过属性动画不断动态获取startD和stopD的值,并更新View

public class LoadingCircleView extends View {

    private Path path;
    private PathMeasure pathMeasure;

    private Path dst; // 被截取的路径

    private Paint paint; // 画笔

    // View的宽高
    private int height;
    private int width;

    private float radius; // 空心圆的半径
    private float mLength; // path路径的长度

    ValueAnimator valueAnimator; // 属性动画
    private float mAnimatorValue; // 属性动画返回的百分比

    private float stop; // 截取路径时的stopD值
    private float start; // 截取路径时的startD值

    public LoadingCircleView(Context context) {
        super(context);
        init(); // 初始化
    }

    public LoadingCircleView(Context context, AttributeSet attrs) {
        super(context, attrs);
        init();
    }

    public LoadingCircleView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        init();
    }

    @TargetApi(Build.VERSION_CODES.LOLLIPOP)
    public LoadingCircleView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
        super(context, attrs, defStyleAttr, defStyleRes);
        init();
    }

    private void init() {
        radius = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP,21,getContext().getResources().getDisplayMetrics()); // 初始化半径
        path = new Path();
        dst = new Path();
        paint = new Paint();
        pathMeasure = new PathMeasure();

        // 设置画笔属性
        paint.setAntiAlias(true);
        paint.setColor(0xbfe46d32);
        paint.setStyle(Paint.Style.STROKE);
        paint.setStrokeCap(Paint.Cap.ROUND);        paint.setStrokeWidth(TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP,3.5f,getContext().getResources().getDisplayMetrics()));

    }

    @Override
    protected void onSizeChanged(int w, int h, int oldw, int oldh) {
        super.onSizeChanged(w, h, oldw, oldh);

        width = w;
        height = h;

        // 勾勒空心圆
        path.reset();
        path.addCircle(width/2,height/2,radius, Path.Direction.CW);

        // 生成pathMeasure对象
        pathMeasure.setPath(path,true);

        // 获取path的长度
        mLength = pathMeasure.getLength();

        // 通过属性动画取得百分比值,并更新View
        valueAnimator = ValueAnimator.ofFloat(0,1);
        valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
            @Override
            public void onAnimationUpdate(ValueAnimator animation) {
                // 获取动画进行的百分比
                mAnimatorValue = (float) animation.getAnimatedValue();
                postInvalidate(); // 更新界面
            }
        });
        // 设置动画的属性
        valueAnimator.setDuration(2100);
        valueAnimator.setRepeatCount(1000);
        valueAnimator.setRepeatMode(ValueAnimator.RESTART);
        valueAnimator.start();
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        int widthSize = MeasureSpec.getSize(widthMeasureSpec);
        int heightSize = MeasureSpec.getSize(heightMeasureSpec);

        // 只对EXACTLY的条件的宽高处理
        setMeasuredDimension(Math.min(widthSize,heightSize),Math.min(widthSize,heightSize));

    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);

        // 为加强动画效果,每次对画布旋转不同角度
        canvas.rotate(360.0f*mAnimatorValue-45.0f,width/2,height/2);
        // 初始化截取的路径
        dst.reset();
        dst.lineTo(0,0); // 消除硬件加速的影响
        // 更新截取的开始值和结束值:当mAnimatorValue为0或1时,两个值相等
        stop = mAnimatorValue*mLength;
        start = (float) (stop - ((0.5 - Math.abs(mAnimatorValue - 0.5)) * mLength));
        // 截取路径后,并绘制路径
        pathMeasure.getSegment(start, stop, dst, true);
        canvas.drawPath(dst,paint);

    }
}

参考:

PathMeasure之迷径追踪

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值