自定义View系列--Path绘制仿支付宝支付成功动画

来源:互联网 发布:js.users.51.la 编辑:程序博客网 时间:2024/05/20 15:48

前言

使用支付宝付款时,我们可以看到成功或者失败都会有个动画提示,如果我们需要做这样的效果的话,当然,你可以让设计师给你做个GIF,但是我们知道图像比较耗内存的,我们自己可以用代码实现还是代码实现好点吧。

效果

这里写图片描述

实现方法

首先我们需要了解PathMeasure这个类,这个类我们可以理解为用来管理Path。我们主要看几个方法。

  • PathMeasure(): 构造方法 ,实例化一个对象

  • PathMeasure(Path path,boolean isClosed):传入Path对象和是否闭合,path对象不能为空

  • getLength():获取当前轮廓、外形的总长度, 如果没有设置Path对象,返回0

  • getSegment(float startD,float stopD,Path dst,boolean startWithMoveTo):调用这个方法,我们可以获取到指定范围内的一段轮廓,存入到dst参数中。所以,这个方法传入的参数分别为长度起始值、结束值、装这一段路径的Path对象、是否MoveTo。另外,这个方法返回值为Boolean类型,如果getLength为0的话,返回false,或者startD > stopD,同样返回false。

  • setPath(Path path , boolean isClosed):给当前PathMeasure对象设置Path

  • nextContour():移动到下一个轮廓

然后我们需要动起来,我们知道invalidate()方法可以刷新界面,也就是重新调用onDraw()方法,所以我们要不停调用invalidate方法,在onDraw方法中改变参数,这样实现动的效果。所以可以用到刚刚介绍的getSegment方法,不断改变获取的范围,从0 * getLength,到1 * getLength,最后绘制完整。所以我们需要一个在一秒内或两秒内一个从0到1的值的变化,so,我们使用ValueAnimator这个类来实现。

//实例化对象mCircleAnimator = ValueAnimator.ofFloat(0, 1);//设置时长为1000msmCircleAnimator.setDuration(1000);//开始动画mCircleAnimator.start();//设置动画监听mCircleAnimator.addUpdateListener(this);

动画开始后,在监听方法中获取当前进度并且重绘图像

mCirclePercent = (float)animation.getAnimatedValue();invalidate();

在onDraw方法中,绘制图像

//画圆mPathCircle.addCircle(getWidth() / 2, getWidth() / 2, getWidth() / 2 - mLineWidth, Path.Direction.CW);mPathMeasure.setPath(mPathCircle, false);mPathMeasure.getSegment(0, mCirclePercent * mPathMeasure.getLength(), mPathCircleDst, true);canvas.drawPath(mPathCircleDst, mPaint);

附上源码,欢迎点评

package com.mintmedical.wavedemo;import android.animation.ValueAnimator;import android.content.Context;import android.graphics.Canvas;import android.graphics.Color;import android.graphics.Paint;import android.graphics.Path;import android.graphics.PathMeasure;import android.util.AttributeSet;import android.util.Log;import android.view.View;/** * Created by MooreLi on 2016/12/12. */public class ResultAnimation extends View implements ValueAnimator.AnimatorUpdateListener {    private Context mContext;    /**     * paint对象     */    private Paint mPaint;    /**     * Path和对应的空Path用来填充     */    private Path mPathCircle;    private Path mPathCircleDst;    private Path mPathRight;    private Path mPathRightDst;    private Path mPathWrong1;    private Path mPathWrong2;    private Path mPathWrong1Dst;    private Path mPathWrong2Dst;    /**     * Path管理     */    private PathMeasure mPathMeasure;    /**     * 动画     */    private ValueAnimator mCircleAnimator;    private ValueAnimator mRightAnimator;    private ValueAnimator mWrong1Animator;    private ValueAnimator mWrong2Animator;    /**     * 当前绘制进度占总Path长度百分比     */    private float mCirclePercent;    private float mRightPercent;    private float mWrong1Percent;    private float mWrong2Percent;    /**     * 线宽     */    private int mLineWidth;    /**     * 正确动画 错误动画     */    public static final int RESULT_RIGHT = 1;    public static final int RESULT_WRONG = 2;    /**     * 当前结果类型     */    private int mResultType = RESULT_WRONG;    public ResultAnimation(Context context) {        super(context);        mContext = context;        init();    }    public ResultAnimation(Context context, AttributeSet attrs) {        super(context, attrs);        mContext = context;        init();    }    public ResultAnimation(Context context, AttributeSet attrs, int defStyleAttr) {        super(context, attrs, defStyleAttr);        mContext = context;        init();    }    private void init() {        mLineWidth = dp2px(3);        mPaint = new Paint();        mPaint.setAntiAlias(true);        mPaint.setStrokeWidth(mLineWidth);        mPaint.setStyle(Paint.Style.STROKE);        mPaint.setColor(Color.GREEN);        initPath();    }    private void initPath() {        mPathCircle = new Path();        mPathCircleDst = new Path();        mPathRight = new Path();        mPathRightDst = new Path();        mPathWrong1 = new Path();        mPathWrong2 = new Path();        mPathWrong1Dst = new Path();        mPathWrong2Dst = new Path();        mPathMeasure = new PathMeasure();        //实例化对象        mCircleAnimator = ValueAnimator.ofFloat(0, 1);        //设置时长为1000ms        mCircleAnimator.setDuration(1000);        //开始动画        mCircleAnimator.start();        //设置动画监听        mCircleAnimator.addUpdateListener(this);        mRightAnimator = ValueAnimator.ofFloat(0, 1);        mRightAnimator.setDuration(500);        mRightAnimator.addUpdateListener(this);        mWrong1Animator = ValueAnimator.ofFloat(0, 1);        mWrong1Animator.setDuration(300);        mWrong1Animator.addUpdateListener(this);        mWrong2Animator = ValueAnimator.ofFloat(0, 1);        mWrong2Animator.setDuration(300);        mWrong2Animator.addUpdateListener(this);    }    @Override    protected void onDraw(Canvas canvas) {        super.onDraw(canvas);        if (mResultType == RESULT_RIGHT) {            mPaint.setColor(Color.GREEN);        } else {            mPaint.setColor(Color.RED);        }        //画圆        mPathCircle.addCircle(getWidth() / 2, getWidth() / 2, getWidth() / 2 - mLineWidth, Path.Direction.CW);        mPathMeasure.setPath(mPathCircle, false);        mPathMeasure.getSegment(0, mCirclePercent * mPathMeasure.getLength(), mPathCircleDst, true);        canvas.drawPath(mPathCircleDst, mPaint);        if (mResultType == RESULT_RIGHT) {            //画对勾            mPathRight.moveTo(getWidth() / 4, getWidth() / 2);            mPathRight.lineTo(getWidth() / 2, getWidth() / 4 * 3);            mPathRight.lineTo(getWidth() / 4 * 3, getWidth() / 4);            if (mCirclePercent == 1) {                mPathMeasure.nextContour();                mPathMeasure.setPath(mPathRight, false);                mPathMeasure.getSegment(0, mRightPercent * mPathMeasure.getLength(), mPathRightDst, true);                canvas.drawPath(mPathRightDst, mPaint);            }        } else {            mPathWrong1.moveTo(getWidth() / 4 * 3, getWidth() / 4);            mPathWrong1.lineTo(getWidth() / 4, getWidth() / 4 * 3);            mPathWrong2.moveTo(getWidth() / 4, getWidth() / 4);            mPathWrong2.lineTo(getWidth() / 4 * 3, getWidth() / 4 * 3);            if (mCirclePercent == 1) {                mPathMeasure.nextContour();                mPathMeasure.setPath(mPathWrong1, false);                mPathMeasure.getSegment(0, mWrong1Percent * mPathMeasure.getLength(), mPathWrong1Dst, true);                canvas.drawPath(mPathWrong1Dst, mPaint);            }            if (mWrong1Percent == 1) {                mPathMeasure.nextContour();                mPathMeasure.setPath(mPathWrong2, false);                mPathMeasure.getSegment(0, mWrong2Percent * mPathMeasure.getLength(), mPathWrong2Dst, true);                canvas.drawPath(mPathWrong2Dst, mPaint);            }        }    }    private int dp2px(int dp) {        float scale = mContext.getResources().getDisplayMetrics().density;        return (int) (scale * dp + 0.5f);    }    @Override    public void onAnimationUpdate(ValueAnimator animation) {        //圆形动画        if (animation.equals(mCircleAnimator)) {            mCirclePercent = (float) animation.getAnimatedValue();            invalidate();            Log.e("TEST","percent:"+mCirclePercent);            if (mCirclePercent == 1) {                if (mResultType == RESULT_RIGHT)                    mRightAnimator.start();                else                    mWrong1Animator.start();            }        }        //正确时  对勾动画        else if (animation.equals(mRightAnimator)) {            mRightPercent = (float) animation.getAnimatedValue();            invalidate();        }        //错误时 右侧动画        else if (animation.equals(mWrong1Animator)) {            mWrong1Percent = (float) animation.getAnimatedValue();            invalidate();            if (mWrong1Percent == 1) {                mWrong2Animator.start();            }        }        //错误时 左侧动画        else if (animation.equals(mWrong2Animator)) {            mWrong2Percent = (float) animation.getAnimatedValue();            invalidate();        }    }    public void setmResultType(int mResultType) {        this.mResultType = mResultType;        invalidate();    }    /**     * 固定写死了宽高,可重新手动调配     *     * @param widthMeasureSpec     * @param heightMeasureSpec     */    @Override    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {        super.onMeasure(widthMeasureSpec, heightMeasureSpec);        setMeasuredDimension(dp2px(50), dp2px(50));    }}

github地址

https://github.com/lizebinbin

欢迎star与fork~

8 0