android Path 和 PathMeasure 进阶

1 概述

在前面的路径和文字中,讲解了path的基本用法,这里讲解一些上篇没有讲到的东西。

2 Path

这里讲解path相关的方法,后面继续讲解PathMeasure,以及实例

(1) offset

public void offset(float dx, float dy)
public void offset(float dx, float dy, Path dst)

这里两个方法都是指定offset,使得path偏移。其中第二个方法中的dst代表了移动后的path写入的目标。如果为null,则会写入调用该方法的path中。

这里使用了第一个方法,代码如下

Path path = new Path();
path.moveTo(0, 0);
path.lineTo(200, 200);
canvas.drawPath(path, paint);

path.offset(500, 0);
canvas.drawPath(path, paint);

再看看第二个方法的用法:

代码如下

Path path = new Path();
path.moveTo(0, 0);
path.lineTo(200, 200);
canvas.drawPath(path, paint);

Path path1 = new Path();
path1.moveTo(0, 0);
path1.lineTo(100, 100);
path1.offset(500, 0, path);
canvas.drawPath(path, paint);

看这两句代码

path1.offset(500, 0, path);
canvas.drawPath(path, paint);

可以看出,偏移后的路径被写入了path中。

(2) FillType

FillType,有点类似前面讲解的xfermode,不过这里比较简单,只有有四个值,如下:

WINDING//默认值,取两个图形相交
EVEN_ODD//取不相交的部分
INVERSE_WINDING//反转相交
INVERSE_EVEN_ODD//反转不相交部分

来看看实验的代码:

Path path = new Path();
path.addRect(100, 100, 500, 500, Path.Direction.CW);
path.addCircle(500, 500, 300, Path.Direction.CW);
path.setFillType(Path.FillType.WINDING);
canvas.drawPath(path, paint);

这里使用的默认的WINDING,来看看各种FillType对应的图形:

WINDING

EVEN_ODD

INVERSE_WINDING

INVERSE_EVEN_ODD

看了上面的图片,四种模式基本就清晰了。

(3) reset

reset放在FillType后面讲解,就是因为它和reset有关系,reset会清空path的所有数据,但是不会清空FillType。我们用一段代码来证实:

paint.setColor(Color.BLUE);
Path path = new Path();
path.addRect(100, 100, 500, 500, Path.Direction.CW);
path.addCircle(500, 500, 300, Path.Direction.CW);
path.setFillType(Path.FillType.INVERSE_WINDING);
path.reset();
canvas.drawPath(path, paint);

看上面的代码,如果FillType被清除,那么这里绘制的将是一个空的内容,整个界面将是白色的,然而图片如下:

这里可以看到,并不是白色,而是蓝色,因为这里的FillType设置的是INVERSE_WINDING,也就是反转,由于它没有被清除掉,那么空路径的补集就是全集,所以整个屏幕被绘制为蓝色。

(4) rewind

rewind和reset类似,但是rewind会清除掉FillType以及所有的直线,曲线,点的数据等,但是他会保留数据结构,这样可以快速重用,提高一定的性能,例如说,重复绘制一类线段,他们的点的数量都相等,那么使用rewind可以保留装载点数据的数据结构,效率会更高。

我们来验证它是否会清除FillType

paint.setColor(Color.BLUE);
Path path = new Path();
path.addRect(100, 100, 500, 500, Path.Direction.CW);
path.addCircle(500, 500, 300, Path.Direction.CW);
path.setFillType(Path.FillType.INVERSE_WINDING);
path.rewind();
canvas.drawPath(path, paint);

效果如下

可以看出,所有数据包括FillType都被清除了。

3 PathMeasure

PathMeasure主要用来测量path,通过它,我们可以得到路径上特定的点的坐标等等。先看看他的基本方法。

(1) 构造方法

public PathMeasure()
public PathMeasure(Path path, boolean forceClosed)

如上,有两个方法,第一个就不讲解了,第二个方法中有两个参数;

path:需要测量的path

forceClosed:是否关闭path

(2) setPath

public void setPath(Path path, boolean forceClosed)

这里就是指定需要测量的path,基本和上面的第二个构造函数类似。

(3) getLength

返回当前path的总长度。

代码如下:

path.addRect(100, 100, 500, 500, Path.Direction.CW);
canvas.drawPath(path, paint);
paint.setStyle(Paint.Style.FILL);
PathMeasure pathMeasure = new PathMeasure(path, false);
float length = pathMeasure.getLength();
canvas.drawText(String.valueOf(length), 500, 500, paint);

可以看到,这里绘制的1600就是这个path轮廓的长度。

(4) getPosTan

public boolean getPosTan(float distance, float pos[], float tan[])

返回值是boolean,如过path为空,则返回false

传入参数有三个:

distance:传入距离起点的距离。

pos[]:意思是position,分别对应点的x,y坐标

tan[]:这个值比较难以理解。我们下面讲解下这个值的意义。

先来看一个动图:

代码如下:

private float[] pos;
private float[] tan;
//绘制动画路径
canvas.drawPath(animPath, paint);

if(distance < pathLength){
//获取位置和
pathMeasure.getPosTan(distance, pos, tan);

matrix.reset();
//计算方位角
float degrees = (float)(Math.atan2(tan[1], tan[0])*180.0/Math.PI);
//计算变换矩阵,用于按照方位角旋转移动的那个图像(矩阵后面有篇幅讲解,这里先不管)
matrix.postRotate(degrees, bm_offsetX, bm_offsetY);
matrix.postTranslate(pos[0]-bm_offsetX, pos[1]-bm_offsetY);

//按照矩阵(包括了旋转和位移)绘制图像
canvas.drawBitmap(bm, matrix, null);

distance += step;
}else{
distance = 0;
}

invalidate();

从上面的图中可以看到,移动的图形,不仅仅只是移动,还进行了旋转,那么这个旋转的角度如何得来,这里就靠这句代码

pathMeasure.getPosTan(distance, pos, tan);

里面的tan就是计算旋转方位角的关键。那么这个tan代表的意义何在呢,我们知道,移动图形最开始的时候的x坐标系是和cavans的平行的,在后面的移动中,这个黑色的火焰进入了一个斜线,然后他发生的旋转,此时,他的x坐标系不再平行于canvas坐标系。这里看一张图:

图中红色的线代表了黑色火焰的x轴,可以看到,他原本是平行于canvas的x轴的,后来在进入第二个斜边的时候,他沿着蓝色箭头旋转了,此时红线不再平行于canvas的x轴,红线和单位圆的交点的x,y坐标就是这里返回的tan值。通过这张图片这里的意义已经非常清晰了。

(5) getMatrix

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

这个方法和上面的其实类似,只是他返回的是一个处理好的matrix,但是这个matrix是以左上角作为旋转点,所以需要将这个点移动到中心点。

其中还多了一个参数flags,指的是这个martrix需要什么信息。flags的值有如下两个

PathMeasure.POSITION_MATRIX_FLAG:位置信息

pathMeasure.TANGENT_MATRIX_FLAG:切边信息,方位角信息,使得图片按path旋转。

代码如下:

matrix.reset();
pathMeasure.getMatrix(distance, matrix, PathMeasure.POSITION_MATRIX_FLAG | pathMeasure.TANGENT_MATRIX_FLAG);
matrix.preTranslate(-bm_offsetX, -bm_offsetY);
canvas.drawBitmap(bm, matrix, null);

其他的相同就不贴了,这里主要是需要做一个旋转点的变换:

matrix.preTranslate(-bm_offsetX, -bm_offsetY);

(6) getSegment

public boolean getSegment(float startD, float stopD, Path dst, boolean startWithMoveTo)

这个方法返回boolean,如果截取的长度为0则返回false,否则为true。参数意义如下

startD:起始距离

stopD:终点距离

dst:接收截取的path

startWithMoveTo:是否把截取的path,moveto到起始点。

来看一个例子:

代码如下

path.addRect(300, 300, 700, 700, Path.Direction.CW);
canvas.drawPath(path, paint);
PathMeasure pathMeasure = new PathMeasure(path, false);

Path dstPath = new Path();
pathMeasure.getSegment(0, 800, dstPath, false);
paint.setColor(Color.RED);
canvas.drawPath(dstPath, paint);

可以看到,由于这里的startWithMoveTo参数设置的是false,那么截取的path就没有moveTo到起始位置,则默认moveTo到(0,0)所以导致了绘制的红线从原点开始。设置了ture之后,图片如下:

可以看到重合了。

好了,Path和PathMeasure就到这里,上面的例子大家可以多多参考。

时间: 2024-08-05 23:15:46

android Path 和 PathMeasure 进阶的相关文章

Android ListView 多种布局--进阶二

Android ListView 多种布局–进阶一 中提及了这么一个需求,本博文就这个需求的实现做进一步探讨. 前面是单列,后面是双列的情况,使用ListView实现,一般的解决思路是处理getView和getCount方法,如下实现: 首先实现Adapter,处理getView和getCount方法 public class DoubleAdapter extends BaseAdapter implements OnClickListener{ private List<String> m

超炫的Android Path Button效果

超炫的Android Path Button效果 一款超炫的Android Path Button效果源码,代码很简洁只有一个文件,改起来很容易,希望能对大家有所帮助. 下载地址:http://www.devstore.cn/code/info/1015.html 运行截图:   

Android Path, Region, Paint, Canvas API篇

从这篇文章开始,准备学习Android Canvas相关的一些知识点,因为Canvas使用的时候还经常要用到Path,Region,Paint.所以这里我们先熟悉Path,Region,Paint,Canvas常用的一些API,为后续的学习做好准备. 在列出Path,Region,Paint,Canvas这些API之前先展示一个具体的实例.一个仪表盘.主要用到的是Canvas的API,和一些三角函数的运算.具体效果图如下 仪表盘分成了三段(2:1:2),每一段显示不同的颜色.刻度分成8大份每小份

android Path类

1.Why 因为需要自己写一个自定义日历控件,所以需要了解一些android图形的基础类. 这篇文章里主要关于Path类的API. 2.moveTo moveTo 不会进行绘制,只用于移动移动画笔. 结合以下方法进行使用. 3.lineTo lineTo 用于进行直线绘制. mPath.lineTo(300, 300); canvas.drawPath(mPath, mPaint); 默认从坐标(0,0)开始绘制.如图: 刚才我们不说了moveTo是用来移动画笔的吗? mPath.moveTo(

Android.mk (2) 函数进阶教程 - 分支、循环、子程序

https://www.jianshu.com/p/674dc7d7b4b0 函数进阶教程 - 分支.循环.子程序 按照面向过程程序设计的标准流程,我们讲完了顺序结构,就要讲分支.循环和子程序.下面我们就开始讲用于分支.循环和子程序调用功能的函数. 分支函数 要走分支,一定是要有条件要判断. 在Makefile里,最主要的判断就是看字符串能不能找到了. 通过findstring函数来进行这个判断,然后用if函数使用findstring函数的结果. 例: .PHONY : all5 bootoat

Android:应用开发进阶必经之路之性能优化(上)

前言 性能优化在一款产品的迭代过程中非常重要:程序实现了功能.还原产品原型只能保证程序能用,但如果要让用户更愿意使用,产品得好用.试想一下如果你开发的产品启动慢.页面显示需要长时间转圈加载.页面切换卡顿.黑白屏.用一会机器就发烫.耗内存.OOM.程序切换到后台后占用内存无法释放......,这些问题就像正在玩游戏时弹出提示框这类糟糕的用户体验一样让用户恼火,如果用户不得不使用你的产品,可能还会一直忍受:但如果有很多同类竞品,糟糕的用户体验会大大影响留存率.有时候产品在市场上的表现差,真不能全怪产

深入了解Android蓝牙Bluetooth——《进阶篇》

在 [深入了解Android蓝牙Bluetooth--<基础篇>](http://blog.csdn.net/androidstarjack/article/details/60468468)一篇中我们对蓝牙的各个版本的有了一个认识,蓝牙版本的历程及其优劣式介绍.那么接下来咱们就深入一点继续开车进入BLE的进及篇章. 蓝牙BLE4.x BLE分为三部分: Service Characteristic Descriptor 这三部分都用UUID作为唯一标识符.UUID为这种格式:0000ffe1

[Android实例] 最全的Android开发资源整理--进阶必备

本帖最后由 一切随枫 于 2014-6-9 12:08 编辑 原文链接: http://stormzhang.github.io/android/2014/06/05/android-awesome-resources/(友情提醒:最近google的很多服务被屏蔽了,可能需要FQ,请自行准备FQ工具) 个人新浪微博:googdev 关注Android.互联网 Android网址或Blog Android官网 身为Android开发者不知道这个网站就太说不过去了,上面有你任何你需要的东西 Andr

[Android]path绘图demo

class MyView extends View { float phase; PathEffect[] effects = new PathEffect[7]; int[] colors; private Paint paint; Path path; public MyView(Context context) { super(context); paint = new Paint(); paint.setStyle(Paint.Style.STROKE); paint.setStroke