王学岗高级UI7——————PathMeasur的实用案例

旋转的箭头

在这里插入图片描述
这里是一个箭头,我们要做的效果就是这个箭头绕着一个圆自动旋转,我们看下代码
方案一:

package com.example.testpathmeasure;

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;

import androidx.annotation.Nullable;

/**
 * @author writing
 * @time 2019/12/25 13:53
 * @note
 */
public class PathMeasureView extends View {
    private float currentValue = 0;//用于记录当前的位置,取值范围为[0,1],映射Path的整个长度
    private float[] pos; //当前点的实际位置
    private float[] tan; // 当前点的tangent值,用于计算图片所需旋转的角度
    private Bitmap mBitmap;//箭头图片
    private Matrix mMatrix; //矩形,用于对图片进行一些操作
    private Paint mDefaultPaint;
    private int mViewWidth;
    private int mViewHeight;
    private Paint mPaint;
    public PathMeasureView(Context context) {
        super(context);
        inint(context);
    }

    public PathMeasureView(Context context, @Nullable AttributeSet attrs) {
        super(context, attrs);
        inint(context);
    }

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

    public PathMeasureView(Context context, @Nullable AttributeSet attrs, int defStyleAttr, int defStyleRes) {
        super(context, attrs, defStyleAttr, defStyleRes);
        inint(context);
    }

    private void inint(Context context) {
        pos = new float[2];
        tan = new float[2];
        BitmapFactory.Options options = new BitmapFactory.Options();
//        options.inSampleSize = 8;//缩放图片
        mBitmap = BitmapFactory.decodeResource(context.getResources(),R.mipmap.arrow,options);
        mMatrix = new Matrix();
        mDefaultPaint = new Paint();
        mDefaultPaint.setColor(Color.RED);
        mDefaultPaint.setStrokeWidth(5);
        mDefaultPaint.setStyle(Paint.Style.STROKE);
        mPaint = new Paint();
        mPaint.setColor(Color.DKGRAY);
        mPaint.setStrokeWidth(2);
        mPaint.setStyle(Paint.Style.STROKE);
    }

    @Override
    protected void onSizeChanged(int w, int h, int oldw, int oldh) {
        super.onSizeChanged(w, h, oldw, oldh);
        //指定View的宽高
        mViewHeight = h;
        mViewWidth = w;
    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        currentValue+=0.005;
        if(currentValue>=1){
            currentValue = 0;
        }
        //平移坐标系,使坐标系位于中心点
        canvas.translate(mViewWidth/2,mViewHeight/2);
        //画坐标轴
        canvas.drawLine(0,-canvas.getHeight(),0,canvas.getHeight(),mPaint);
        canvas.drawLine(-canvas.getWidth(),0,canvas.getWidth(),0,mPaint);
        Path path = new Path();
        //顺时针绘制圆
        path.addCircle(0,0,300, Path.Direction.CW);
        canvas.drawPath(path,mDefaultPaint);
        PathMeasure pathMeasure = new PathMeasure(path,false);
        //获取指定长度位置的矩阵
        pathMeasure.getMatrix(pathMeasure.getLength()*currentValue,mMatrix,PathMeasure.TANGENT_MATRIX_FLAG|PathMeasure.POSITION_MATRIX_FLAG);
        //设置矩阵偏移,不然纸飞机的尖不会对准圆圈。设置矩阵偏移的时候要把矩阵的坐标系与cavans的坐标系保持一致。
        //保证矩阵的坐标系平行于屏幕的长和宽。当矩阵旋转的时候,矩阵的坐标系也会旋转,这一点要牢记。
        mMatrix.preTranslate(-mBitmap.getWidth()/2,-mBitmap.getHeight()/2);
        //绘制飞机
        canvas.drawBitmap(mBitmap,mMatrix,mDefaultPaint);
        invalidate();
    }
}

运行的效果,飞机在不停的旋转
在这里插入图片描述
在这里插入图片描述
方案二,与方案一效果一模一样

package com.example.testpathmeasure;


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;

import androidx.annotation.Nullable;

/**
 * @author writing
 * @time 2019/12/25 13:53
 * @note
 */
public class PathMeasureView1 extends View {
    private float currentValue = 0;//用于记录当前的位置,取值范围为[0,1],映射Path的整个长度
    private float[] pos; //当前点的实际位置
    private float[] tan; // 当前点的tangent值,用于计算图片所需旋转的角度
    private Bitmap mBitmap;//箭头图片
    private Matrix mMatrix; //矩形,用于对图片进行一些操作
    private Paint mDefaultPaint;
    private int mViewWidth;
    private int mViewHeight;
    private Paint mPaint;

    public PathMeasureView1(Context context) {
        super(context);
        inint(context);
    }

    public PathMeasureView1(Context context, @Nullable AttributeSet attrs) {
        super(context, attrs);
        inint(context);
    }

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

    public PathMeasureView1(Context context, @Nullable AttributeSet attrs, int defStyleAttr, int defStyleRes) {
        super(context, attrs, defStyleAttr, defStyleRes);
        inint(context);
    }

    private void inint(Context context) {
        pos = new float[2];
        tan = new float[2];
        BitmapFactory.Options options = new BitmapFactory.Options();
//        options.inSampleSize = 8;//缩放图片
        mBitmap = BitmapFactory.decodeResource(context.getResources(), R.mipmap.arrow, options);
        mMatrix = new Matrix();
        mDefaultPaint = new Paint();
        mDefaultPaint.setColor(Color.RED);
        mDefaultPaint.setStrokeWidth(5);
        mDefaultPaint.setStyle(Paint.Style.STROKE);
        mPaint = new Paint();
        mPaint.setColor(Color.DKGRAY);
        mPaint.setStrokeWidth(2);
        mPaint.setStyle(Paint.Style.STROKE);
    }

    @Override
    protected void onSizeChanged(int w, int h, int oldw, int oldh) {
        super.onSizeChanged(w, h, oldw, oldh);
        //指定View的宽高
        mViewHeight = h;
        mViewWidth = w;
    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        currentValue += 0.005;
        if (currentValue >= 1) {
            currentValue = 0;
        }
        canvas.translate(mViewWidth/2,mViewHeight/2);
        canvas.drawLine(canvas.getWidth(),0,-canvas.getWidth(),0,mPaint);
        canvas.drawLine(0,canvas.getWidth(),0,-canvas.getWidth(),mPaint);
        Path path = new Path();
        path.addCircle(0, 0, 300, Path.Direction.CW);
        canvas.drawPath(path,mPaint);
        PathMeasure pathMeasure = new PathMeasure(path,false);
        pathMeasure.getPosTan(pathMeasure.getLength()*currentValue,pos,tan);
        mMatrix.reset();
        //Math.atan2(x,y)*180/Math.PI表示x,y这一点和原点连线的正切值。如Math.atan2(9,9)*180/Math.PI的值就是45,即9/9的正切值的角的度数
        float degree = (float) (Math.atan2(tan[1],tan[0])*180/Math.PI);
        //旋转图片
        mMatrix.postRotate(degree,mBitmap.getWidth()/2,mBitmap.getHeight()/2);
        //将图片中心调整到与当前点重合
        mMatrix.postTranslate(pos[0]-mBitmap.getWidth()/2,pos[1]-mBitmap.getWidth()/2);
        canvas.drawPath(path,mPaint);
        canvas.drawBitmap(mBitmap,mMatrix,mDefaultPaint);

        invalidate();
    }
}

转动的loading

package com.example.testpathmeasure;

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

import androidx.annotation.Nullable;

/**
 * @author writing
 * @time 2019/12/26 12:55
 * @note
 */
public class LoadingView extends View {
    private Path mPath;
    private Paint mPaint;
    private PathMeasure mPathMeasure;
    private float mAnimatorValue;
    private Path mDst;
    private float mLength;
    public LoadingView(Context context) {
        this(context,null);
    }

    public LoadingView(Context context, @Nullable AttributeSet attrs) {
        this(context, attrs,-1);
    }

    public LoadingView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
        this(context, attrs, defStyleAttr,-1);
    }

    public LoadingView(Context context, @Nullable AttributeSet attrs, int defStyleAttr, int defStyleRes) {
        super(context, attrs, defStyleAttr, defStyleRes);
        innit();
    }

    private void innit() {
        mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
        mPaint.setStyle(Paint.Style.STROKE);
        mPaint.setStrokeWidth(5);
        mPath = new Path();
        mPath.addCircle(400,400,100, Path.Direction.CCW);
        mPathMeasure = new PathMeasure(mPath,true);
        mLength = mPathMeasure.getLength();
        mDst = new Path();
        startAnima();
    }

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

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        mDst.reset();
        //避免硬件加速带来的问题
        mDst.lineTo(0,0);
        float end = mLength*mAnimatorValue;
        float start = (float) (end-((0.5-Math.abs(mAnimatorValue-0.5))*mLength));
        Log.i("zhang_xin","start:"+start);
        mPathMeasure.getSegment(start,end,mDst,true);
        canvas.drawPath(mDst,mPaint);
    }
}

小船随波浪线动

package com.dn_alan.myapplication;

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.Log;
import android.view.View;
import android.view.animation.LinearInterpolator;

/**
 * 小船滑动
 */
public class WaveView extends View {
    private static final int INT_WAVE_LENGTH = 1000;
    private static final String TAG = "WaveView";

    private Path mPath;
    private Paint mPaint;

    private int mDeltaX;
    private Bitmap mBitMap;
    private PathMeasure mPathMeasure;
    private float[] pos;
    private float[] tan;
    private Matrix mMatrix;
    private float faction;

    public WaveView(Context context) {
        super(context);
        init();

    }

    private void init() {
        mPaint = new Paint();
        mPaint.setColor(Color.RED);
        mPaint.setStyle(Paint.Style.STROKE);

        mPath = new Path();
        pos = new float[2];
        tan = new float[2];
        mMatrix = new Matrix();

        BitmapFactory.Options options = new BitmapFactory.Options();
        options.inSampleSize =  2;
        mBitMap = BitmapFactory.decodeResource(getResources(),R.drawable.timg,options);
    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        mPath.reset();
        int orginY = 800;
        int halfWaveLength = INT_WAVE_LENGTH / 2;
        mPath.moveTo(-INT_WAVE_LENGTH + mDeltaX, orginY);
        for(int i = -INT_WAVE_LENGTH ; i < getWidth() + INT_WAVE_LENGTH;
            i += INT_WAVE_LENGTH){
            mPath.rQuadTo(halfWaveLength/2,120,halfWaveLength,0);
            mPath.rQuadTo(halfWaveLength/2,-120,halfWaveLength,0);
        }
        mPath.lineTo(getWidth(),getHeight());
        mPath.lineTo(0,getHeight());
        mPath.close();



        canvas.drawPath(mPath,mPaint);

        mPathMeasure = new PathMeasure(mPath,false);
        float length = mPathMeasure.getLength();
        mMatrix.reset();
        boolean posTan = mPathMeasure.getPosTan(length*faction,pos,tan);


        canvas.drawCircle(tan[0],tan[1],20,mPaint);
        canvas.drawLine(tan[0],tan[1],pos[0],pos[1],mPaint);

        Log.i(TAG,"----------------------pos[0] = " + pos[0] + "pos[1] = " +pos[1]);
        Log.i(TAG,"----------------------tan[0] = " + tan[0] + "tan[1] = " +tan[1]);
        if(posTan){
            // 方案一 :自己计算
            // 将tan值通过反正切函数得到对应的弧度,在转化成对应的角度度数
            /*float degrees = (float) (Math.atan2(tan[1],tan[0])*180f / Math.PI);
            mMatrix.postRotate(degrees, mBitMap.getWidth()/2, mBitMap.getHeight() / 2);
            mMatrix.postTranslate(pos[0]- mBitMap.getWidth() / 2,pos[1] - mBitMap.getHeight());
            canvas.drawBitmap(mBitMap,mMatrix,mPaint);*/

            // 方案二 :直接使用API
            //直接帮你算了角度并且帮你进行了旋转
            mPathMeasure.getMatrix(length*faction, mMatrix, PathMeasure.TANGENT_MATRIX_FLAG | PathMeasure.POSITION_MATRIX_FLAG);
            mMatrix.preTranslate(- mBitMap.getWidth() / 2, - mBitMap.getHeight());
            canvas.drawBitmap(mBitMap,mMatrix,mPaint);
        }

        //

    }

    public void startAnimation(){
        //波浪动
//        ValueAnimator anim = ValueAnimator.ofInt(0,INT_WAVE_LENGTH);
//        anim.setDuration(1000);
//        anim.setInterpolator(new LinearInterpolator());
//        anim.setRepeatCount(ValueAnimator.INFINITE);
//        anim.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
//            @Override
//            public void onAnimationUpdate(ValueAnimator animation) {
//                mDeltaX = (int) animation.getAnimatedValue();
//                postInvalidate();
//            }
//        });
//        anim.start();

        ValueAnimator animator = ValueAnimator.ofFloat(0,1);
        animator.setDuration(10000);
        animator.setInterpolator(new LinearInterpolator());
        animator.setRepeatCount(ValueAnimator.INFINITE);
        animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
            @Override
            public void onAnimationUpdate(ValueAnimator animation) {
                faction  = (float) animation.getAnimatedValue();
                Log.i(TAG,"----------------------faction = " + faction);
                postInvalidate();
            }
        });
        animator.start();
    }
}

package com.dn_alan.myapplication;

import androidx.appcompat.app.AppCompatActivity;

import android.os.Bundle;
import android.view.View;

public class MainActivity extends AppCompatActivity {
    private WaveView mWaveView;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        mWaveView = new WaveView(this);
        setContentView(mWaveView);
        mWaveView.startAnimation();
    }
}

发布了208 篇原创文章 · 获赞 15 · 访问量 7万+

猜你喜欢

转载自blog.csdn.net/qczg_wxg/article/details/103704423