仿支付宝支付成功打勾动画(关于PathMeasure你该知道的东西)

Android路径动画

沿预定路径生成动画图像(无论是直线或曲线)是做动画时一种常见的情况。传统方法是寻找Path的函数(贝塞尔函数用的比较多),带入x的值来计算y的值, 然后使用坐标(x,y)来完成动画。
现在还有一种更简单的方法:通过使用path和PathMeasure来完成。
Path大家都很熟悉了,那PathMeasure是什么东西呢,从命名上就可以看出,这相当于Path坐标计算器。以前我们需要知道路径函数的算法才能算坐标,现在PathMeasure帮我们处理了这一过程。现在来讲讲它的使用方法

初始化

PathMeasure pathMeasure = new PathMeasure();

PathMeasure提供了一个setPath()将path和pathMeasure进行绑定

pathMeasure.setPath(path,false);

当然也可通过初始化的时候就指定:

PathMeasure pathMeasure= new PathMeasure(Path path,boolean forceClosed);

forceClosed决定是否强制闭合,设置为true的话,path首位最尾在measure时会闭合。

forceClosed参数对绑定的Path不会产生任何影响,例如一个折线段的Path,本身是没有闭合的,forceClosed设置为True的时候,PathMeasure计算的Path是闭合的,但Path本身绘制出来是不会闭合的。

forceClosed参数对PathMeasure的测量结果有影响,还是例如前面说的一个折线段的Path,本身没有闭合,forceClosed设置为True,PathMeasure的计算就会包含最后一段闭合的路径,与原来的Path不同。

API提供的其它重要方法

  • float getLength():获取路径长度
  • boolean getPosTan(float distance, float[] pos, float[] tan) 传入一个大于0小于getLength()的获取坐标点和切线的坐标,放入post[]和tan[]中
  • boolean getSegment(float startD, float stopD, Path dst, boolean startWithMoveTo) 传入一个开始点和结束点, 然后会返回介于这之间的Path,当作dst。startWithMoveTo通常为True,保证每次截取的Path片段都是正常的、完整的
  • boolean nextContour 当需要传入多个path的时候进行切换用的,contour时是轮廓的意思

支付动画简单版——打勾动画

PathMeasure到底是怎么做的呢,我们先以一个支付动画的简单版来说明,在这个实例中,要完成的操作就是画一个圆,在圆圈里面进行动态打勾。这里用的的是getSegment不断绘制path的每一段,直到绘制完成。
怎么控制进度呢,我们定义了一个tickPercent来标记进度的百分比,通过valueAnimator来实时更新进度来绘制,具体的实现代码如下:

public class CircleTickView extends View {


    private PathMeasure tickPathMeasure;
    /**
     * 打钩百分比
     */
    float tickPercent = 0;

    private Path path;
    //初始化打钩路径
    private Path tickPath;


    // 圆圈的大小,半径
    private int circleRadius;
    private int circleColor;
    private int circleStrokeWidth;

    private RectF rec;
    private Paint tickPaint;


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

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

    public CircleTickView(Context context) {
        super(context);
        init(context, null);
    }

    public void init(Context context, AttributeSet attrs) {

        TypedArray mTypedArray = context.obtainStyledAttributes(attrs, R.styleable.CircleTickView);
        circleRadius = mTypedArray.getInteger(R.styleable.CircleTickView_circleRadius, 150);
        circleColor = mTypedArray.getColor(R.styleable.CircleTickView_circleViewColor, ContextCompat.getColor(context, R.color.colorPrimary));
        circleStrokeWidth = mTypedArray.getInteger(R.styleable.CircleTickView_circleStrokeWidth, 20);
        mTypedArray.recycle();

        tickPaint = new Paint();
        tickPathMeasure = new PathMeasure();
        rec = new RectF();
        path = new Path();
        tickPath = new Path();
        tickPaint.setStyle(Paint.Style.STROKE);
        tickPaint.setAntiAlias(true);
        tickPaint.setColor(circleColor);
        tickPaint.setStrokeWidth(circleStrokeWidth);

        //打钩动画
        ValueAnimator mTickAnimation;
        mTickAnimation = ValueAnimator.ofFloat(0f, 1f);
        mTickAnimation.setStartDelay(1000);
        mTickAnimation.setDuration(500);
        mTickAnimation.setInterpolator(new AccelerateInterpolator());
        mTickAnimation.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
            @Override
            public void onAnimationUpdate(ValueAnimator animation) {
                tickPercent = (float) animation.getAnimatedValue();
                invalidate();
            }
        });
        mTickAnimation.start();
    }

    @Override
    protected void onDraw(Canvas canvas) {

        int width = canvas.getWidth();
        int height = canvas.getHeight();

        // 根据设置该view的高度,进行对所画图进行居中处理
        int offsetHeight = (height - circleRadius * 2) / 2;

        // 设置第一条直线的相关参数
        int firStartX = width / 2 - circleRadius * 3 / 5;
        int firStartY = offsetHeight + circleRadius;

        int firEndX = (width / 2 - circleRadius / 5) - 1;
        int firEndY = offsetHeight + circleRadius + circleRadius / 2 + 1;


        int secEndX = width / 2 + circleRadius * 3 / 5;
        int secEndY = offsetHeight + circleRadius / 2;


        rec.set(width / 2 - circleRadius, offsetHeight, width / 2 + circleRadius, offsetHeight + circleRadius * 2);
        tickPath.moveTo(firStartX, firStartY);
        tickPath.lineTo(firEndX, firEndY);
        tickPath.lineTo(secEndX, secEndY);
        tickPathMeasure.setPath(tickPath, false);
        /*
         * On KITKAT and earlier releases, the resulting path may not display on a hardware-accelerated Canvas.
         * A simple workaround is to add a single operation to this path, such as dst.rLineTo(0, 0).
         */
        tickPathMeasure.getSegment(0, tickPercent * tickPathMeasure.getLength(), path, true);
        path.rLineTo(0, 0);
        canvas.drawPath(path, tickPaint);
        canvas.drawArc(rec, 0, 360, false, tickPaint);
    }


}

想写这么多了,有空再把全真的支付宝支付动画po上来。

你可能感兴趣的:(Android之路)