利用pathMeasure实现路径动画
package com.loaderman.customviewdemo; import android.animation.ValueAnimator; import android.content.Context; import android.graphics.*; import android.util.AttributeSet; import android.view.View; public class GetSegmentView extends View { private Path mCirclePath, mDstPath; private Paint mPaint; private PathMeasure mPathMeasure; private Float mCurAnimValue; public GetSegmentView(Context context, AttributeSet attrs) { super(context, attrs); setLayerType(LAYER_TYPE_SOFTWARE, null); mPaint = new Paint(Paint.ANTI_ALIAS_FLAG); mPaint.setStyle(Paint.Style.STROKE); mPaint.setStrokeWidth(4); mPaint.setColor(Color.BLACK); mDstPath = new Path(); mCirclePath = new Path(); mCirclePath.addCircle(100, 100, 50, Path.Direction.CW); mPathMeasure = new PathMeasure(mCirclePath, true); ValueAnimator animator = ValueAnimator.ofFloat(0, 1); animator.setRepeatCount(ValueAnimator.INFINITE); animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { 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 = length * mCurAnimValue; float start = (float) (stop - ((0.5 - Math.abs(mCurAnimValue - 0.5)) * length)); mDstPath.reset(); canvas.drawColor(Color.WHITE); mPathMeasure.getSegment(start, stop, mDstPath, true);//用于截取整个path中某个片段,通过参数startD和stopD来控制截取的长度,并将截取后的path保存到参数dst中,最后一个参数表示起始点是否使用moveTo将路径的新起始点移到结果path的起始点中,通常设置为true // mPathMeasure.getSegment(0, stop, mDstPath, true); canvas.drawPath(mDstPath, mPaint); } }
package com.loaderman.customviewdemo; 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; public class AliPayView extends View { private Path mCirclePath, mDstPath; private Paint mPaint; private PathMeasure mPathMeasure; private Float mCurAnimValue; private int mCentX = 100; private int mCentY = 100; private int mRadius = 50; public AliPayView(Context context, AttributeSet attrs) { super(context, attrs); setLayerType(LAYER_TYPE_SOFTWARE, null); mPaint = new Paint(Paint.ANTI_ALIAS_FLAG); mPaint.setStyle(Paint.Style.STROKE); mPaint.setStrokeWidth(4); mPaint.setColor(Color.BLACK); mDstPath = new Path(); mCirclePath = new Path(); mCirclePath.addCircle(mCentX, mCentY, mRadius, Path.Direction.CW); mCirclePath.moveTo(mCentX - mRadius / 2, mCentY); mCirclePath.lineTo(mCentX, mCentY + mRadius / 2); mCirclePath.lineTo(mCentX + mRadius / 2, mCentY - mRadius / 3); mPathMeasure = new PathMeasure(mCirclePath, false); ValueAnimator animator = ValueAnimator.ofFloat(0, 2); animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { public void onAnimationUpdate(ValueAnimator animation) { mCurAnimValue = (Float) animation.getAnimatedValue(); invalidate(); } }); animator.setDuration(4000); animator.start(); } boolean mNext = false; @Override protected void onDraw(Canvas canvas) { super.onDraw(canvas); canvas.drawColor(Color.WHITE); if (mCurAnimValue < 1) { float stop = mPathMeasure.getLength() * mCurAnimValue; mPathMeasure.getSegment(0, stop, mDstPath, true); } else { if (!mNext) { mNext = true; mPathMeasure.getSegment(0, mPathMeasure.getLength(), mDstPath, true); mPathMeasure.nextContour(); //跳转到下一条曲线函数 } float stop = mPathMeasure.getLength() * (mCurAnimValue - 1); mPathMeasure.getSegment(0, stop, mDstPath, true); } canvas.drawPath(mDstPath, mPaint); } }
package com.loaderman.customviewdemo; import android.animation.ValueAnimator; import android.content.Context; import android.graphics.Bitmap; import android.graphics.BitmapFactory; 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; public class GetPosTanView extends View { private Path mCirclePath, mDstPath; private Paint mPaint; private PathMeasure mPathMeasure; private Float mCurAnimValue; private Bitmap mArrawBmp; private float[] pos = new float[2]; private float[] tan = new float[2]; public GetPosTanView(Context context, AttributeSet attrs) { super(context, attrs); setLayerType(LAYER_TYPE_SOFTWARE, null); mArrawBmp = BitmapFactory.decodeResource(getResources(), R.drawable.arraw); mPaint = new Paint(Paint.ANTI_ALIAS_FLAG); mPaint.setStyle(Paint.Style.STROKE); mPaint.setStrokeWidth(4); mPaint.setColor(Color.BLACK); mDstPath = new Path(); mCirclePath = new Path(); mCirclePath.addCircle(100, 100, 50, Path.Direction.CW); mPathMeasure = new PathMeasure(mCirclePath, true);//true计算的path的闭合长度,false则测量当前path状态长度 ValueAnimator animator = ValueAnimator.ofFloat(0, 1); animator.setRepeatCount(ValueAnimator.INFINITE);//无限循环 animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { public void onAnimationUpdate(ValueAnimator animation) { mCurAnimValue = (Float) animation.getAnimatedValue(); invalidate(); } }); animator.setDuration(2000); animator.start(); } @Override protected void onDraw(Canvas canvas) { super.onDraw(canvas); canvas.drawColor(Color.WHITE); float length = mPathMeasure.getLength(); //计算路径长度 float stop = length * mCurAnimValue; mDstPath.reset(); mPathMeasure.getSegment(0, stop, mDstPath, true); canvas.drawPath(mDstPath, mPaint); /** * 箭头旋转、位移实现方式一: */ //计算方位角 // mPathMeasure.getPosTan(stop, pos, tan);//用于得到路径上某一长度的位置,以及位置的证正切值 // float degrees = (float) (Math.atan2(tan[1], tan[0]) * 180.0 / Math.PI); // Matrix matrix = new Matrix(); // matrix.postRotate(degrees, mArrawBmp.getWidth() / 2, mArrawBmp.getHeight() / 2); // matrix.postTranslate(pos[0] - mArrawBmp.getWidth() / 2, pos[1] - mArrawBmp.getHeight() / 2); /** * 箭头旋转、位移实现方式一: */ Matrix matrix = new Matrix(); mPathMeasure.getMatrix(stop, matrix, PathMeasure.POSITION_MATRIX_FLAG | PathMeasure.TANGENT_MATRIX_FLAG);//用于的到路径上某一长度的位置以及该位置的正切值的矩阵 matrix.preTranslate(-mArrawBmp.getWidth() / 2, -mArrawBmp.getHeight() / 2); canvas.drawBitmap(mArrawBmp, matrix, mPaint); } }
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:orientation="vertical" android:layout_width="fill_parent" android:layout_height="fill_parent"> <com.loaderman.customviewdemo.GetSegmentView android:layout_width="match_parent" android:layout_height="80dp"/> <com.loaderman.customviewdemo.AliPayView android:layout_width="match_parent" android:layout_height="80dp"/> <com.loaderman.customviewdemo.GetPosTanView android:layout_width="match_parent" android:layout_height="150dp"/> </LinearLayout>
效果
最后,关注【码上加油站】微信公众号后,有疑惑有问题想加油的小伙伴可以码上加入社群,让我们一起码上加油吧!!!