利用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 GetSegmentViewContext context, AttributeSet attrs) {
        supercontext, attrs);
        setLayerTypeLAYER_TYPE_SOFTWARE, null);

        mPaint = new PaintPaint.ANTI_ALIAS_FLAG);
        mPaint.setStylePaint.Style.STROKE);
        mPaint.setStrokeWidth4);
        mPaint.setColorColor.BLACK);

        mDstPath = new Path);
        mCirclePath = new Path);
        mCirclePath.addCircle100, 100, 50, Path.Direction.CW);

        mPathMeasure = new PathMeasuremCirclePath, true);

        ValueAnimator animator = ValueAnimator.ofFloat0, 1);
        animator.setRepeatCountValueAnimator.INFINITE);
        animator.addUpdateListenernew ValueAnimator.AnimatorUpdateListener) {
            public void onAnimationUpdateValueAnimator animation) {
                mCurAnimValue = Float) animation.getAnimatedValue);
                invalidate);
            }
        });
        animator.setDuration2000);
        animator.start);
    }

    @Override
    protected void onDrawCanvas canvas) {
        super.onDrawcanvas);
        float length = mPathMeasure.getLength);
        float stop = length * mCurAnimValue;
        float start = float) stop - 0.5 - Math.absmCurAnimValue - 0.5)) * length));
        mDstPath.reset);
        canvas.drawColorColor.WHITE);
        mPathMeasure.getSegmentstart, stop, mDstPath, true);//用于截取整个path中某个片段,通过参数startD和stopD来控制截取的长度,并将截取后的path保存到参数dst中,最后一个参数表示起始点是否使用moveTo将路径的新起始点移到结果path的起始点中,通常设置为true

//        mPathMeasure.getSegment0, stop, mDstPath, true);
        canvas.drawPathmDstPath, 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 AliPayViewContext context, AttributeSet attrs) {
        supercontext, attrs);
        setLayerTypeLAYER_TYPE_SOFTWARE, null);

        mPaint = new PaintPaint.ANTI_ALIAS_FLAG);
        mPaint.setStylePaint.Style.STROKE);
        mPaint.setStrokeWidth4);
        mPaint.setColorColor.BLACK);

        mDstPath = new Path);
        mCirclePath = new Path);

        mCirclePath.addCirclemCentX, mCentY, mRadius, Path.Direction.CW);

        mCirclePath.moveTomCentX - mRadius / 2, mCentY);
        mCirclePath.lineTomCentX, mCentY + mRadius / 2);
        mCirclePath.lineTomCentX + mRadius / 2, mCentY - mRadius / 3);

        mPathMeasure = new PathMeasuremCirclePath, false);

        ValueAnimator animator = ValueAnimator.ofFloat0, 2);
        animator.addUpdateListenernew ValueAnimator.AnimatorUpdateListener) {
            public void onAnimationUpdateValueAnimator animation) {
                mCurAnimValue = Float) animation.getAnimatedValue);
                invalidate);
            }
        });
        animator.setDuration4000);
        animator.start);
    }

    boolean mNext = false;

    @Override
    protected void onDrawCanvas canvas) {
        super.onDrawcanvas);
        canvas.drawColorColor.WHITE);

        if mCurAnimValue < 1) {
            float stop = mPathMeasure.getLength) * mCurAnimValue;
            mPathMeasure.getSegment0, stop, mDstPath, true);
        } else {
            if !mNext) {
                mNext = true;
                mPathMeasure.getSegment0, mPathMeasure.getLength), mDstPath, true);
                mPathMeasure.nextContour);  //跳转到下一条曲线函数
            }
            float stop = mPathMeasure.getLength) * mCurAnimValue - 1);
            mPathMeasure.getSegment0, stop, mDstPath, true);
        }
        canvas.drawPathmDstPath, 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 GetPosTanViewContext context, AttributeSet attrs) {
        supercontext, attrs);
        setLayerTypeLAYER_TYPE_SOFTWARE, null);
        mArrawBmp = BitmapFactory.decodeResourcegetResources), R.drawable.arraw);
        mPaint = new PaintPaint.ANTI_ALIAS_FLAG);
        mPaint.setStylePaint.Style.STROKE);
        mPaint.setStrokeWidth4);
        mPaint.setColorColor.BLACK);

        mDstPath = new Path);
        mCirclePath = new Path);
        mCirclePath.addCircle100, 100, 50, Path.Direction.CW);

        mPathMeasure = new PathMeasuremCirclePath, true);//true计算的path的闭合长度,false则测量当前path状态长度

        ValueAnimator animator = ValueAnimator.ofFloat0, 1);
        animator.setRepeatCountValueAnimator.INFINITE);//无限循环
        animator.addUpdateListenernew ValueAnimator.AnimatorUpdateListener) {
            public void onAnimationUpdateValueAnimator animation) {
                mCurAnimValue = Float) animation.getAnimatedValue);
                invalidate);
            }
        });
        animator.setDuration2000);
        animator.start);
    }

    @Override
    protected void onDrawCanvas canvas) {
        super.onDrawcanvas);

        canvas.drawColorColor.WHITE);
        float length = mPathMeasure.getLength);  //计算路径长度
        float stop = length * mCurAnimValue;
        mDstPath.reset);

        mPathMeasure.getSegment0, stop, mDstPath, true);
        canvas.drawPathmDstPath, mPaint);


        /**
         * 箭头旋转、位移实现方式一:
         */

        //计算方位角
//        mPathMeasure.getPosTanstop, pos, tan);//用于得到路径上某一长度的位置,以及位置的证正切值
//        float degrees = float) Math.atan2tan[1], tan[0]) * 180.0 / Math.PI);
//        Matrix matrix = new Matrix);
//        matrix.postRotatedegrees, mArrawBmp.getWidth) / 2, mArrawBmp.getHeight) / 2);
//        matrix.postTranslatepos[0] - mArrawBmp.getWidth) / 2, pos[1] - mArrawBmp.getHeight) / 2);

        /**
         * 箭头旋转、位移实现方式一:
         */
        Matrix matrix = new Matrix);
        mPathMeasure.getMatrixstop, matrix, PathMeasure.POSITION_MATRIX_FLAG | PathMeasure.TANGENT_MATRIX_FLAG);//用于的到路径上某一长度的位置以及该位置的正切值的矩阵
        matrix.preTranslate-mArrawBmp.getWidth) / 2, -mArrawBmp.getHeight) / 2);
        canvas.drawBitmapmArrawBmp, 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>

效果

Published by

风君子

独自遨游何稽首 揭天掀地慰生平