41

自定义控件神器--PathMeasure

 5 years ago
source link: https://chsmy.github.io/2019/04/13/technology/自定义控件神器-PathMeasure/?amp%3Butm_medium=referral
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

自定义控件神器–PathMeasure

我们平时自定义View的时候,经常会绘制path,想要求出path路径上的某个点的位置,很难通过一般的数学函数来计算,幸好系统给我们提供了一个PathMeasure可以用来测量一个path的路径,我们可以根据测量得到的值来绘制一些别的效果。

PathMeasure有两个构造方法

public PathMeasure() {
      mPath = null;
      native_instance = native_create(0, false);
  }
public PathMeasure(Path path, boolean forceClosed) {
      mPath = path;
      native_instance = native_create(path != null ? path.readOnlyNI() : 0,forceClosed);
  }

一个是有参数的,一个是没参数的

pathMeasure.setPath(mPath,false);

下面来看PathMeasure中的几个比较常用的方法

getSegment

给定一个起始点,一个结束点和一个空path对象,把给定的起点到终点的路径赋值给这个空的path。 public boolean getSegment(float startD, float stopD, Path dst, boolean startWithMoveTo) 。最后一个参数startWithMoveTo表示起点是否使用moveTo,用来保证截取的path第一个点的位置不变。从而保证截取的片段不会变形

它返回的是一个boolean值,如果截取到的路径长度是0就返回false,不是0就返回true。

下面来看一下使用getSegment的简单案例,在onDraw方法中

mPath.reset();
mFloat += 0.01;
if (mFloat >= 1){
    mFloat = 0;
}
mPath.addCircle(getWidth()/2,getHeight()/2,200,Path.Direction.CW);
mDst.reset();
pathMeasure.setPath(mPath,false);
float distance = pathMeasure.getLength() * mFloat;
pathMeasure.getSegment(0, distance , mDst, true);
canvas.drawPath(mDst, mPaint);
invalidate();

使用一个0-1的成员变量mFloat来控制当前截取比例,每次调用invalidate的时候mFloat的值都会改变,这样就可以弄个简单的动画效果 效果如下

jUruuyi.gif

上面一直是从0来时截取如果我们改一下截取的位置

pathMeasure.getSegment(2*distance/3, distance, mDst, true);

第一个参数改成了 2*distance/3 ,效果如下,类似一个加载动画的效果,这个参数可以随便改,更改的不同截取效果就不同

yQFfeyb.gif

前面两个我们只绘制了我们截取的path,下面把原path也绘制上,截取的部分换一种颜色绘制,效果如下

IZfmi2j.gif

getPosTan

getPosTan方法,可以获得path路径上某个点的位置和这个点的切线的值。 public boolean getPosTan(float distance, float pos[], float tan[]) distance是当前需要截取的长度,pos是一个数组,如果不为空,可以给这个数组赋值,pos[0]是x坐标pos[1]是y坐标,tan跟也是个数组,tan[0],tan[1]代表切线的值,通过Math.atan2()方法传入tan[0],tan[1]可以获取到当前切线的角度。

下面看一个简单的例子

mPath.reset();

  mFloat += 0.01;
  if (mFloat >= 1){
      mFloat = 0;
  }
 mPath.addCircle(getWidth()/2,getHeight()/2,200,Path.Direction.CW);
  pathMeasure.setPath(mPath,false);
 //用来记录位置
  float []pos = new float[2];
  //用来记录切点的位置
  float []tan = new float[2];
  float distance = pathMeasure.getLength() * mFloat;
  pathMeasure.getPosTan(distance,pos,tan);
  //计算出当前图片要旋转的角度
  float degree = (float) (Math.atan2(tan[1], tan[0]) * 180 / Math.PI);
  mMatrix.reset();
  //设置旋转角度和旋转中心
  mMatrix.postRotate(degree,mBitmap.getWidth() / 2,mBitmap.getHeight() / 2);
  //设置绘制的中心点与当前图片中心点重合
  mMatrix.postTranslate(pos[0]-mBitmap.getWidth() / 2,pos[1]-mBitmap.getHeight()/2);
  canvas.drawPath(mPath, mPaint);
  canvas.drawBitmap(mBitmap,mMatrix, mPaint);

效果如下:

yYFvIff.gif

通过getPosTan方法,获取到当前的角度,通过Matrix方法把一个图片旋转这个角度,然后绘制到圆圈上

除了使用getPosTan之外,还有一个稍微简单的方法可以获取到pos和tan的值,通过 pathMeasure.getMatrix 方法

public boolean getMatrix(float distance, Matrix matrix, int flags)

前两个值好理解,最后一个值flag,用来指定矩阵中需要返回那些参数,有两个值

public static final int POSITION_MATRIX_FLAG = 0x01;

public static final int TANGENT_MATRIX_FLAG = 0x02;

这两个值分别代表前面的pos和tan,例如下面的代码

mPath.reset();
 mFloat += 0.01;
 if (mFloat >= 1){
     mFloat = 0;
 }
 //路径
 mPath.lineTo(0, 200);
 mPath.lineTo(300, 200);
 mPath.quadTo(450,100,600,200);
 mPath.lineTo(900, 200);
 pathMeasure.setPath(mPath,false);
 //将pos信息和tan信息保存在mMatrix中
 pathMeasure.getMatrix(pathMeasure.getLength() * mFloat, mMatrix,
         PathMeasure.POSITION_MATRIX_FLAG | PathMeasure.TANGENT_MATRIX_FLAG);
 //将图片的旋转坐标调整到图片中心位置
 mMatrix.preTranslate(-mBitmap.getWidth() / 2, -mBitmap.getHeight() / 2);
 canvas.drawPath(mPath, mPaint);
 canvas.drawBitmap(mBitmap,mMatrix, mPaint);

效果:

qURZrui.gif

源码地址


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK