您当前的位置: 首页 >  动画
  • 0浏览

    0关注

    674博文

    0收益

  • 0浏览

    0点赞

    0打赏

    0留言

私信
关注
热门博文

Android Path以及PathMeasure动画练习

沙漠一只雕得儿得儿 发布时间:2020-04-19 22:07:04 ,浏览量:0

本次练习效果图如下:左边是支付宝的支付成功打对勾的path动画,右边的是一个箭头沿着圆圈做圆周运动的path动画。

           

首先了解下PathMeasure的相关基础函数的用法,下图是将一个正方形,利用PathMeasure的getSegment()方法截取从0开始,到150像素距离长度的路径画出来如下:

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.view.View;

import androidx.annotation.Nullable;

public class GetSegmentViewLearn extends View {

    Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG);
    Path originPath = new Path();
    Path dstPath = new Path();
    PathMeasure measure;

    public GetSegmentViewLearn(Context context) {
        super(context);
    }

    public GetSegmentViewLearn(Context context, @Nullable AttributeSet attrs) {
        super(context, attrs);

        paint.setStyle(Paint.Style.STROKE);
        paint.setStrokeWidth(5);
        paint.setColor(Color.BLACK);

        originPath.addRect(-50, -50, 50, 50, Path.Direction.CCW);
        measure = new PathMeasure(originPath, false);
    }

    /**
     *
     * getSegment函数使用:用于截取整个Path中的某个片段,并将截取后的Path保存到参数dstPath中
     *
     *
     * @param canvas
     */

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

        canvas.translate(100, 100);
        measure.getSegment(0, 150, dstPath, true);
        canvas.drawPath(dstPath, paint);
    }
}

能够利用path以及PathMeasure画出静态路径的图后,动态路径的图其实跟简单,就是不断改变getSegment函数中的起点和终点坐标即可,下面画一个动态的圆,就是上面动图中所示的外面那个圆圈:

import android.animation.ValueAnimator;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Matrix;
import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.PathMeasure;
import android.util.AttributeSet;
import android.view.View;

import com.test.cp5_pathmeasure.util.Utils;

import androidx.annotation.Nullable;

public class GetSegmentView extends View {

    private Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG);
    private PathMeasure mPathMeasure;
    private Path mDstPath = new Path();
    private Float mCurAnimValue;

    //箭头动画
    private Bitmap mArrawBmp;
    private float[] pos = new float[2];
    private float[] tan = new float[2];

    public GetSegmentView(Context context, @Nullable AttributeSet attrs) {
        super(context, attrs);

        mArrawBmp = Utils.changeBitmapSize(getResources(), R.drawable.arraw);

        paint.setStyle(Paint.Style.STROKE);
        paint.setStrokeWidth(5);
        paint.setColor(Color.BLACK);
        Path mCirclePath = new Path();
        mCirclePath.addCircle(100, 100, 50, Path.Direction.CW);

        mPathMeasure = new PathMeasure(mCirclePath, false);

        ValueAnimator animator = ValueAnimator.ofFloat(0, 1);
        animator.setRepeatCount(ValueAnimator.INFINITE);
        animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
            @Override
            public void onAnimationUpdate(ValueAnimator animation) {
                mCurAnimValue = (float) animation.getAnimatedValue();
                invalidate();
            }
        });
        animator.setDuration(2000);
        animator.start();
    }


    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        float length = mPathMeasure.getLength();
        float stop = mCurAnimValue * length;
        float start = (float) (stop - ((0.5 - Math.abs(mCurAnimValue - 0.5)) * length));

        /**
         * 画出一个圆圈,终点动态变化
         * 想要动态画出截取到的path路线,只要不断改变终点的取值即可
         */
        mDstPath.reset();
        mPathMeasure.getSegment(0, stop, mDstPath, true);
        canvas.drawPath(mDstPath, paint);

        /**
         * 画出一个圆圈,起点、终点动态变化
         * 想要动态画出截取到的path路线,只要不断改变起点和终点的取值即可
         */
//        mDstPath.reset();
//        mPathMeasure.getSegment(start, stop, mDstPath, true);
//        canvas.drawPath(mDstPath, paint);

        /**
         * 箭头旋转、位移实现方式一:
         *
         */
//        mPathMeasure.getPosTan(stop, pos, tan);
//        float degrees = (float) (Math.atan2(tan[1], tan[0]) * 180 / Math.PI);
//        Matrix matrix = new Matrix();
//        matrix.postRotate(degrees, mArrawBmp.getWidth()/2, mArrawBmp.getHeight()/2);
//        matrix.postTranslate(pos[0] - (float) mArrawBmp.getWidth() / 2, pos[1] - (float) mArrawBmp.getHeight() / 2);
//        canvas.drawBitmap(mArrawBmp, matrix, paint);

        /**
         * 箭头旋转、位移实现方式二:
         */
        Matrix matrix2 = new Matrix();
        mPathMeasure.getMatrix(stop, matrix2, PathMeasure.POSITION_MATRIX_FLAG | PathMeasure.TANGENT_MATRIX_FLAG);
        matrix2.preTranslate(-mArrawBmp.getWidth() / 2, -mArrawBmp.getHeight() / 2);
        canvas.drawBitmap(mArrawBmp, matrix2, paint);
    }
}

其中的箭头图形有个工具类:

import android.content.res.Resources;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Matrix;
import android.util.Log;

public class Utils {

    public static Bitmap changeBitmapSize(Resources res, int id) {
        Bitmap bitmap = BitmapFactory.decodeResource(res, id);
        int width = bitmap.getWidth();
        int height = bitmap.getHeight();
        Log.e("width", "width:" + width);
        Log.e("height", "height:" + height);

        //设置想要的大小
        int newWidth = 30;
        int newHeight = 30;

        //计算压缩的比率
        float scaleWidth = ((float) newWidth) / width;
        float scaleHeight = ((float) newHeight) / height;

        //获取想要缩放的matrix
        Matrix matrix = new Matrix();
        matrix.postScale(scaleWidth, scaleHeight);

        //获取新的bitmap
        bitmap = Bitmap.createBitmap(bitmap, 0, 0, width, height, matrix, true);
        bitmap.getWidth();
        bitmap.getHeight();
        Log.e("newWidth", "newWidth" + bitmap.getWidth());
        Log.e("newHeight", "newHeight" + bitmap.getHeight());
        return bitmap;

    }
}

支付宝的动图,其实就是利用path的两个路径,利用pathMeasure的nextContour函数就可以跳转到path路径的下一个路径上去:

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.view.View;

import androidx.annotation.Nullable;

public class AliPayView extends View {

    Paint mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);

    Path mCirclePath = new Path();
    Path mCircleDstPath = new Path();
    PathMeasure mMeasure;

    float mFraction = 0;

    public AliPayView(Context context, @Nullable AttributeSet attrs) {
        super(context, attrs);

        mPaint.setStrokeWidth(5);
        mPaint.setColor(Color.BLACK);
        mPaint.setStyle(Paint.Style.STROKE);

        int mCentX = 100;
        int mCentY = 100;
        int mRadius = 50;
        //先画一个圆形O
        mCirclePath.addCircle(mCentX, mCentY, mRadius, Path.Direction.CW);
        //再将path移动到画对勾的地方画√
        mCirclePath.moveTo(mCentX - mRadius / 2, mCentY);
        mCirclePath.lineTo(mCentX, mCentY + mRadius / 2);
        mCirclePath.lineTo(mCentX + mRadius / 2, mCentY - mRadius / 3);

        mMeasure = new PathMeasure(mCirclePath, false);

        ValueAnimator animator = ValueAnimator.ofFloat(0, 2);
        animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
            @Override
            public void onAnimationUpdate(ValueAnimator animation) {
                mFraction = (float) animation.getAnimatedValue();
                invalidate();
            }
        });
        animator.setDuration(4000);
        animator.start();
    }


    boolean mNext = false;
    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        float length = mMeasure.getLength();
        if (mFraction < 1) {
            float stop = length * mFraction;
            mMeasure.getSegment(0, stop, mCircleDstPath, true);
        } else {
            if (!mNext) {
                mNext = true;
                mMeasure.getSegment(0, length, mCircleDstPath, true);
                mMeasure.nextContour();
            }
            float stop = length * (mFraction - 1);
            mMeasure.getSegment(0, stop, mCircleDstPath, true);
        }
        canvas.drawPath(mCircleDstPath, mPaint);
    }
}

 

关注
打赏
1657159701
查看更多评论
立即登录/注册

微信扫码登录

0.0376s