PathMeasure基本使用

PathMeasure从名字就可以看出,这个类应该关联一个path类对象。它是对它关联path对象的进一步测量。所以PathMeasure与Path是一一对应的,一个PathMeasure对象如果没有与他关联的Path对象,那么这个PathMeasure也就没什么存在的意义了。它提供了测量path长度,返回一个点的坐标和正切值等方法。


PathMeasure基本使用_第1张图片
PathMeasure提供的方法
  • 构造函数(Constructors)

  • 无参构造函数:仅仅是构造了一个PathMeasure对象,并没有关联任何path。要想使用它可以调用setPath传入想要关联的path。
  • 有参构造函数:接收一个path对象与之关联,这个path对象就是我们想要测量的实体。第二个boolean类型的forceClosed的意义。如果传入的是true,那么即使传入的path不是闭合的,那么也强制认为它的闭合的。如果是false,就不会又任何变化。一般使用的时候都会传入false。
  • 公共方法

  • getLength:返回关联path当前轮廓的长度,如果没有关联path,将会返回0;
  • getMatrix:传入的distance要大于0,并且小于path当前轮廓的长度。它会计算distance在Path上对应的点的信息,并将信息封装到传入的martrix中。通过返回的boolean值可以判断计算是否成功。
  • getPosTan:传入的distance要大于0,并且小于path当前轮廓的长度。它会计算distance在Path上对应的点的信息,并将信息封装到float[] pos和float[] tan中,他们分别是一个长度为2的数组,数组中index=0是x方向上的信息,index=1是y方向上的信息。通过返回的boolean值可以判断计算是否成功。
  • getSegment:startD和stopD分别是起始距离和终止距离,此方法会裁剪取出他们中间的path放到dst中。startD和stopD都应该是合法的值,否则返回false。裁剪出来的path长度为0也会返回false,如果是正常的情况,就会返回true。startWithMoveTo为true表示截取到的path的第一个点不变。如果为false,截取到的path的起点会连接到dst之前到的最后一个点。
  • isClosed:当前轮廓是否是闭合的
  • nextContour:移动到path中的下一个轮廓上
  • setPath:设置与之有关的path.第二个boolean类型的forceClosed的意义。如果传入的是true,那么即使传入的path不是闭合的,那么也强制认为它的闭合的。如果是false,就不会又任何变化。一般使用的时候都会传入false。

方法就这么几个,而且很简单。接下来利用这几个方法,来做两个炫酷的效果。

旋转小飞机

PathMeasure基本使用_第2张图片
小飞机沿着红圈当前点的切线方向旋转飞行
public class PlaneView extends View{

    private Paint mPaint;
    private int mWidth,mHeight;
    private Path mPath;
    private Bitmap mBitmap;
    private int mBitmapWidth,mBitmapHeight;
    private Matrix mMatrix;
    private float[] pos;
    private float[] tan;
    private PathMeasure pathMeasure;
    private float totalLength,currentLength;

    public PlaneView(Context context) {
        this(context,null);
    }

    public PlaneView(Context context, @Nullable AttributeSet attrs) {
        this(context, attrs,0);
    }

    public PlaneView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        init();
    }

    private void init() {
        pos = new float[2];
        tan = new float[2];

        mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
        mPaint.setStrokeWidth(4);
        mPaint.setStyle(Paint.Style.STROKE);
        mPaint.setColor(Color.RED);
        mPath = new Path();
        // 获取缩放的小飞机图片
        BitmapFactory.Options options = new BitmapFactory.Options();
        options.inSampleSize = 8;
        mBitmap = BitmapFactory.decodeResource(getResources(), R.drawable.arrow,options);
        mBitmapWidth = mBitmap.getWidth();
        mBitmapHeight = mBitmap.getHeight();

        mMatrix = new Matrix();
    }

    @Override
    protected void onSizeChanged(int w, int h, int oldw, int oldh) {
        super.onSizeChanged(w, h, oldw, oldh);
        mWidth = getWidth();
        mHeight = getHeight();
        mPath.addCircle(mWidth/2,mHeight/2,220, Path.Direction.CW);
        // 创建一个PathMeasure与Path关联
        pathMeasure = new PathMeasure(mPath,false);
        // 获取Path的总长度
        totalLength = pathMeasure.getLength();
    }

    @Override
    protected void onDraw(Canvas canvas) {
        // 画坐标轴
        canvas.drawLine(mWidth/2,0,mWidth/2,mHeight,mPaint);
        canvas.drawLine(0,mHeight/2,mWidth,mHeight/2,mPaint);
        // 画圆圈
        canvas.drawPath(mPath,mPaint);

        // 获取当前点的信息
        boolean posTan = pathMeasure.getPosTan(currentLength, pos, tan);
        if (posTan){
            mMatrix.reset();
            // 计算图片要旋转的角度
            float degrees = (float) (Math.atan2(tan[1], tan[0]) * 180.0 / Math.PI);
            // 旋转图片
            mMatrix.postRotate(degrees,mBitmapWidth / 2,mBitmapHeight / 2);
            // 将图片中心绘制到当前点
            mMatrix.postTranslate(pos[0] - mBitmapWidth / 2,pos[1] - mBitmapHeight / 2);
            canvas.drawBitmap(mBitmap,mMatrix,mPaint);
            currentLength += 5;
            if (currentLength >= totalLength){
                currentLength = 0.0f;
            }
            invalidate();
        }
    }
}

一个正在加载中的小特效

PathMeasure基本使用_第3张图片
public class CircleView extends View {

    private int mWidth,mHeight;
    private Path mPath,mDst;
    private Paint mPaint;
    private PathMeasure pathMeasure;
    private float animatedValue;

    public CircleView(Context context) {
        this(context,null);
    }

    public CircleView(Context context, @Nullable AttributeSet attrs) {
        this(context, attrs,0);
    }

    public CircleView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        init();
    }

    private void init() {
        mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
        mPaint.setStyle(Paint.Style.STROKE);
        mPaint.setStrokeWidth(5);

        mPath = new Path();
        mDst = new Path();
        // 执行动画
        final ValueAnimator valueAnimator = ValueAnimator.ofFloat(0,1);
        valueAnimator.setDuration(2000);
        valueAnimator.setRepeatCount(ValueAnimator.INFINITE);
        valueAnimator.start();
        valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
            @Override
            public void onAnimationUpdate(ValueAnimator animation) {
                animatedValue = (float) animation.getAnimatedValue();
                invalidate();
            }
        });
    }

    @Override
    protected void onSizeChanged(int w, int h, int oldw, int oldh) {
        mWidth = getWidth();
        mHeight = getHeight();

        mPath.addCircle(mWidth / 2,mHeight /2,220, Path.Direction.CW);
        pathMeasure = new PathMeasure(mPath,false);
    }

    @Override
    protected void onDraw(Canvas canvas) {
        mDst.reset();
        // 计算新path的终点位置
        float stop = pathMeasure.getLength() * animatedValue;
        // 计算新path的起点位置
        float start = (float) (stop - ((0.5 - Math.abs(animatedValue - 0.5)) * pathMeasure.getLength()));
        // 根据计算的值截取出新Path
        boolean segment = pathMeasure.getSegment(start, stop, mDst, true);
        if (segment){
            canvas.drawPath(mDst,mPaint);
        }
    }
}

小船随波逐流

PathMeasure基本使用_第4张图片
public class WaveView extends View {

    private static int DRAW_BOAT_OFFSET = 15;
    private Bitmap bitmap;
    private int mWaveLength;
    private Path mPath;
    private int mHeight;
    private int mWidth;
    private Paint mPaint;
    private Matrix mMatrix;
    private PathMeasure pathMeasure;
    private float animatedValue;

    public WaveView(Context context) {
        this(context,null);
    }

    public WaveView(Context context, @Nullable AttributeSet attrs) {
        this(context, attrs,0);
    }

    public WaveView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        init();
    }

    private void init() {
        // 加载小船图片
        BitmapFactory.Options options = new BitmapFactory.Options();
        options.inSampleSize = 2;
        bitmap = BitmapFactory.decodeResource(getResources(), R.drawable.timg, options);

        // 初始化需要用到的变量
        mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
        mPaint.setColor(Color.argb(105,0,0,255));
        mPaint.setStyle(Paint.Style.FILL);
        mPath = new Path();
        mMatrix = new Matrix();
        pathMeasure = new PathMeasure();
    }

    @Override
    protected void onSizeChanged(int w, int h, int oldw, int oldh) {
        mWidth = getWidth();
        mHeight = getHeight();
        mWaveLength = mWidth / 2;
    }

    @Override
    protected void onDraw(Canvas canvas) {
        mPath.reset();
        // 根据动画的进行进度设置波浪形的path路径,并绘制波浪
        mPath.moveTo(-mWaveLength+(mWaveLength/2)*animatedValue,mHeight/2);
        for (int i = -mWaveLength; i < mWidth + mWaveLength; i+=mWaveLength) {
            mPath.rQuadTo(mWaveLength / 2 +(mWaveLength/2)*animatedValue,
                    60,
                    mWaveLength+(mWaveLength/2)*animatedValue,
                    0);
            mPath.rQuadTo(mWaveLength / 2 +(mWaveLength/2)*animatedValue,
                    -60,
                    mWaveLength+(mWaveLength/2)*animatedValue,
                    0);
        }
        mPath.lineTo(mWidth,mHeight);
        mPath.lineTo(0,mHeight);
        mPath.close();
        canvas.drawPath(mPath,mPaint);

        // 因为每次绘制的path可能都不同,所以每次都为pathMeasure设置path
        pathMeasure.setPath(mPath,false);
        // 根据动画进行的进度取出当前长度的matrix
        boolean matrix = pathMeasure.getMatrix(animatedValue * pathMeasure.getLength(), mMatrix,
                PathMeasure.TANGENT_MATRIX_FLAG | PathMeasure.POSITION_MATRIX_FLAG);
        if (matrix){
            // 操作matrix,绘制小船
            mMatrix.preTranslate(-bitmap.getWidth() / 2,-bitmap.getHeight() + DRAW_BOAT_OFFSET);
            canvas.drawBitmap(bitmap,mMatrix,null);
        }
    }

    // 开启动画
    public void startAnimator(){
        ValueAnimator valueAnimator = ValueAnimator.ofFloat(0,1);
        valueAnimator.setDuration(15000);
        valueAnimator.setInterpolator(new LinearInterpolator());
        valueAnimator.setRepeatCount(INFINITE);
        valueAnimator.start();
        valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
            @Override
            public void onAnimationUpdate(ValueAnimator animation) {
                animatedValue = (float) animation.getAnimatedValue();
                invalidate();
            }
        });
    }
}

你可能感兴趣的:(PathMeasure基本使用)