什么是PathMeasure
PathMeasure是用来对Path相关数据进行测量的工具类
它可以测出Path的长度,点位置,切线斜率,判断路径是否闭合,截取子路径等功能
PathMeasure函数接口
//绑定Path,如果曲线没有闭合,可以通过forceClosed强制闭合
public void setPath(Path path, boolean forceClosed);
//计算路径总长度
public float getLength();
//获取路径指定位置的坐标和切线方向
//这个位置,是通过指定点到起点的路径长度来表示的
//pos中的值为[x,y],tan中的值为[Δx,Δy]
boolean getPosTan(float distance, float pos[], float tan[]);
//获取指定位置的坐标和正切
//可以通过flag指定全部获取,还是只获取其中一个
//flags = PathMeasure.POSITION_MATRIX_FLAG | PathMeasure.TANGENT_MATRIX_FLAG
//位置信息存到矩阵中,可用来进行平移变换,正切信息存到矩阵中,可用来进行旋转变换
boolean getMatrix(float distance, Matrix matrix, int flags);
//截取start-end位置的子路径,存入dst变量
//start和end是点到路径起点的总距离
//注意,这个函数是将新的子路径,直接拼接到dst路径上面,它并不会清除dst路径中已有的数据
//如果需要清除dst路径中的已有数据,需要自己调用reset方法
//startWithMoveTo=true,表示通过moveTo的方式连接新路径
//startWithMoveTo=false,表示通过lineTo的方式连接新路径
public boolean getSegment(float startD, float stopD, Path dst, boolean startWithMoveTo)
//如果一个路径是不连续的,则它会包含多个子路径
//getSegment和getLength等,默认是在首个子路径上计算长度和截取部分
//如果想在其它子路径上操作,则需要调用nextContour,调至下个子路径
public boolean nextContour()
//判断当前子路径是否闭合
boolean isClosed();
PathMeasure应用案例
核心代码
package com.android.architecture;
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 android.view.animation.AccelerateDecelerateInterpolator;
//进度加载动画
@SuppressWarnings("all")
public class B extends View {
Paint paint = new Paint();
Path srcPath = new Path();
Path dstPath = new Path();
PathMeasure pathMeasure = new PathMeasure();
float progress;
float length;
public B(Context context) {
this(context, null);
}
public B(Context context, AttributeSet attributeSet) {
super(context, attributeSet);
init(context, attributeSet);
//开始进度动画
//进度动画有加速减速过程,比我们自己去匀速控制进度更自然
ValueAnimator anim = ValueAnimator.ofFloat(0, 1);
anim.addUpdateListener(animation -> {
progress = (float) animation.getAnimatedValue();
invalidate();
});
anim.setDuration(2000);
anim.setRepeatCount(ValueAnimator.INFINITE);
anim.setInterpolator(new AccelerateDecelerateInterpolator());
anim.start();
}
protected void init(Context context, AttributeSet attributeSet) {
//关闭硬件加速
setLayerType(LAYER_TYPE_SOFTWARE, null);
//创建画笔
paint.setAntiAlias(true);
paint.setColor(Color.RED);
paint.setStrokeWidth(10);
paint.setStyle(Paint.Style.STROKE);
//完整路径
srcPath.addCircle(400, 400, 200, Path.Direction.CCW);
//计算路径总长度
pathMeasure.setPath(srcPath, true);
length = pathMeasure.getLength();
}
@Override
protected void onDraw(Canvas canvas) {
//画局部弧
double arcRatio = 0.5 - Math.abs(0.5 - progress);
double stop = length * progress;
double start = stop - length * arcRatio;
dstPath.reset();
pathMeasure.getSegment((float) start, (float) stop, dstPath, true);
canvas.drawPath(dstPath, paint);
}
}
package com.android.architecture;
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;
//阿童木沿圆形太阳漫游动画
@SuppressWarnings("all")
public class A extends View {
Bitmap bitmap;
Paint paint;
Matrix matrix = new Matrix();
float progress = 0;
public A(Context context) {
this(context, null);
}
public A(Context context, AttributeSet attributeSet) {
super(context, attributeSet);
init(context, attributeSet);
}
private void init(Context context, AttributeSet attributeSet) {
//图片缩小
BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = true;
BitmapFactory.decodeResource(context.getResources(), R.drawable.atm, options);
options.inSampleSize = options.outWidth / 50;
options.inJustDecodeBounds = false;
bitmap = BitmapFactory.decodeResource(context.getResources(), R.drawable.atm, options);
//创建画笔
paint = new Paint();
paint.setAntiAlias(true);
paint.setColor(Color.RED);
paint.setStrokeWidth(5);
paint.setStyle(Paint.Style.STROKE);
}
@Override
protected void onDraw(Canvas canvas) {
//计算动画进度
progress += 0.005;
if (progress >= 1)
progress = 0;
//画底色
canvas.drawColor(Color.WHITE);
//从中间开始画
canvas.translate(getMeasuredWidth() / 2, getMeasuredHeight() / 2);
//顺时针画圆
Path path = new Path();
path.addCircle(0, 0, 200, Path.Direction.CW);
//获得圆上指定点的位置和正切
//这两个属性,可以作为图片的平移距离和旋转角度来使用
//Path上点的位置,就是绘图时,图片左上角的位置,点切线的方向,就是图片旋转的方向
PathMeasure pathMeasure = new PathMeasure(path, false);
float distance = pathMeasure.getLength() * progress;
pathMeasure.getMatrix(distance, matrix, PathMeasure.POSITION_MATRIX_FLAG | PathMeasure.TANGENT_MATRIX_FLAG);
//动画左下角与圆吻合,因此要先平移一下,调整绘图的起点位置
matrix.preTranslate(0, -bitmap.getHeight());
//画平移旋转后的图像
canvas.drawPath(path, paint);
canvas.drawBitmap(bitmap, matrix, paint);
//刷新下一帧
postInvalidateDelayed(30);
}
}
package com.android.architecture;
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.MotionEvent;
import android.view.View;
import android.view.animation.LinearInterpolator;
//阿童木沿波浪冲锋
@SuppressWarnings("all")
public class C extends View {
public static final int WAVE_LENGTH = 600;
Bitmap bitmap;
Paint paint = new Paint();
Path path = new Path();
PathMeasure pathMeasure;
Matrix matrix = new Matrix();
ValueAnimator anim;
float progress;
int positive = 1;
public C(Context context) {
this(context, null);
}
public C(Context context, AttributeSet attributeSet) {
super(context, attributeSet);
init(context, attributeSet);
}
protected void init(Context context, AttributeSet attributeSet) {
//图片缩小
BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = true;
BitmapFactory.decodeResource(context.getResources(), R.drawable.atm, options);
options.inSampleSize = options.outWidth / 50;
options.inJustDecodeBounds = false;
bitmap = BitmapFactory.decodeResource(context.getResources(), R.drawable.atm, options);
//创建画笔
paint.setAntiAlias(true);
paint.setColor(Color.RED);
paint.setStrokeWidth(5);
paint.setStyle(Paint.Style.STROKE);
//开始进度动画
anim = ValueAnimator.ofFloat(0, 1);
anim.setDuration(8000);
anim.setRepeatMode(ValueAnimator.REVERSE);
anim.setRepeatCount(ValueAnimator.INFINITE);
anim.setInterpolator(new LinearInterpolator());
anim.addUpdateListener(animation -> {
float old = progress;
progress = (float) animation.getAnimatedValue();
if (progress > old)
positive = 1;
if (progress
关注
打赏
最近更新
- 深拷贝和浅拷贝的区别(重点)
- 【Vue】走进Vue框架世界
- 【云服务器】项目部署—搭建网站—vue电商后台管理系统
- 【React介绍】 一文带你深入React
- 【React】React组件实例的三大属性之state,props,refs(你学废了吗)
- 【脚手架VueCLI】从零开始,创建一个VUE项目
- 【React】深入理解React组件生命周期----图文详解(含代码)
- 【React】DOM的Diffing算法是什么?以及DOM中key的作用----经典面试题
- 【React】1_使用React脚手架创建项目步骤--------详解(含项目结构说明)
- 【React】2_如何使用react脚手架写一个简单的页面?