安卓自定义View进阶-Path之基本操作

来源:互联网 发布:比邻软件 编辑:程序博客网 时间:2024/05/17 09:35

转载GcsSloop

一.Path常用方法表

本表格中去除了部分API21(即安卓版本5.0)以上才添加的方法。
这里写图片描述
这里写图片描述

二.Path详解

请关闭硬件加速,以免引起不必要的问题!

Path作用

本次特地开了一篇详细讲解Path,为什么要单独摘出来呢,这是因为Path在2D绘图中是一个很重要的东西。

Path含义

Path封装了由直线和曲线(二次,三次贝塞尔曲线)构成的几何路径。你能用Canvas中的drawPath来把这条路径画出来(同样支持Paint的不同绘制模式),也可以用于剪裁画布和根据路径绘制文字。我们有时会用Path来描述一个图像的轮廓,所以也会称为轮廓线(轮廓线仅是Path的一种使用方法,两者并不等价)

Path使用方法详解

第1组: moveTo、 setLastPoint、 lineTo 和 close

先创建画笔:
Paint mPaint = new Paint(); // 创建画笔
mPaint.setColor(Color.BLACK); // 画笔颜色 - 黑色
mPaint.setStyle(Paint.Style.STROKE); // 填充模式 - 描边
mPaint.setStrokeWidth(10); // 边框宽度 - 10

lineTo:

lineTo是指从某个点到参数坐标点之间连一条线,这里的某个点就是上次操作结束的点,如果没有进行过操作则默认点为坐标原点。
这里写图片描述

这里写图片描述
在示例中我们调用了两次lineTo,第一次由于之前没有过操作,所以默认点就是坐标原点O,结果就是坐标原点O到A(200,200)之间连直线(用蓝色圈1标注)。

第二次lineTo的时候,由于上次的结束位置是A(200,200),所以就是A(200,200)到B(200,0)之间的连线(用蓝色圈2标注)。

moveTo 和 setLastPoint:

public void moveTo (float x, float y)

public void setLastPoint (float dx, float dy)

这两个方法虽然在作用上有相似之处,但实际上却是完全不同的两个东东,具体参照下表:

这里写图片描述

这里写图片描述

这里写图片描述

moveTo只改变下次操作的起点,在执行完第一次LineTo的时候,本来的默认点位置是A(200,200),但是moveTo将其改变成为了C(200,100),所以在第二次调用lineTo的时候就是连接C(200,100) 到 B(200,0) 之间的直线(用蓝色圈2标注)。

下面是setLastPoint的示例:

这里写图片描述

这里写图片描述

setLastPoint是重置上一次操作的最后一个点,在执行完第一次的lineTo的时候,最后一个点是A(200,200),而setLastPoint更改最后一个点为C(200,100),所以在实际执行的时候,第一次的lineTo就不是从原点O到A(200,200)的连线了,而变成了从原点O到C(200,100)之间的连线了。

在执行完第一次lineTo和setLastPoint后,最后一个点的位置是C(200,100),所以在第二次调用lineTo的时候就是C(200,100) 到 B(200,0) 之间的连线(用蓝色圈2标注)。

close

方法预览:

public void close ()

close方法用于连接当前最后一个点和最初的一个点(如果两个点不重合的话),最终形成一个封闭的图形。

这里写图片描述

这里写图片描述

很明显,两个lineTo分别代表第1和第2条线,而close在此处的作用就算连接了B(200,0)点和原点O之间的第3条线,使之形成一个封闭的图形。

注意:close的作用是封闭路径,与连接当前最后一个点和第一个点并不等价。如果连接了最后一个点和第一个点仍然无法形成封闭图形,则close什么 也不做。

第2组: addXxx与arcTo

这次内容主要是在Path中添加基本图形,重点区分addArc与arcTo。

第一类(基本形状)

// 圆形
public void addCircle (float x, float y, float radius, Path.Direction dir)
// 椭圆
public void addOval (RectF oval, Path.Direction dir)
// 矩形
public void addRect (float left, float top, float right, float bottom, Path.Direction dir)
public void addRect (RectF rect, Path.Direction dir)
// 圆角矩形
public void addRoundRect (RectF rect, float[] radii, Path.Direction dir)
public void addRoundRect (RectF rect, float rx, float ry, Path.Direction dir)

Direction的意思是 方向,趋势。 点进去看一下会发现Direction是一个枚举(Enum)类型,里面只有两个枚举常量

这里写图片描述

这里写图片描述

这里写图片描述

我们先分析一下,绘制一个矩形(仅绘制边线),实际上只需要进行四次lineTo操作就行了,也就是说,只需要知道4个点的坐标,然后使用moveTo到第一个点,之后依次lineTo就行了(从上面的测试可以看出,在实际绘制中也确实是这么干的)。

可是为什么要这么做呢?确定一个矩形最少需要两个点(对角线的两个点),根据这两个点的坐标直接算出四条边然后画出来不就行了,干嘛还要先计算出四个点坐标,之后再连直线呢?

这个就要涉及一些path的存储问题了,前面在path中的定义中说过,Path是封装了由直线和曲线(二次,三次贝塞尔曲线)构成的几何路径。其中曲线部分用的是贝塞尔曲线,稍后再讲。 然而除了曲线部分就只剩下直线了,对于直线的存储最简单的就是记录坐标点,然后直接连接各个点就行了。虽然记录矩形只需要两个点,但是如果只用两个点来记录一个矩形的话,就要额外增加一个标志位来记录这是一个矩形,显然对于存储和解析都是很不划算的事情,将矩形转换为直线,为的就是存储记录方便。

对于上面这个矩形来说,我们采用的是顺时针(CW),所以记录的点的顺序就是 A -> B -> C -> D. 最后一个点就是D,我们这里使用setLastPoint改变最后一个点的位置实际上是改变了D的位置。

理解了上面的原理之后,设想如果我们将顺时针改为逆时针(CCW),则记录点的顺序应该就是 A -> D -> C -> B, 再使用setLastPoint则改变的是B的位置,我们试试看结果和我们的猜想是否一致:

这里写图片描述

第二类(Path)

方法预览:
public void addPath (Path src)
public void addPath (Path src, float dx, float dy)
public void addPath (Path src, Matrix matrix)

这个相对比较简单,也很容易理解,就是将两个Path合并成为一个。

第三个方法是将src添加到当前path之前先使用Matrix进行变换。

第二个方法比第一个方法多出来的两个参数是将src进行了位移之后再添加进当前path中。

示例:

这里写图片描述

这里写图片描述

首先我们新建地方两个Path(矩形和圆形)中心都是坐标原点,我们在将包含圆形的path添加到包含矩形的path之前将其进行移动了一段距离,最终绘制出来的效果就如上面所示。

第三类(addArc与arcTo)

方法预览:

public void addArc (RectF oval, float startAngle, float sweepAngle)
public void arcTo (RectF oval, float startAngle, float sweepAngle)
public void arcTo (RectF oval, float startAngle, float sweepAngle, boolean forceMoveTo)

这里写图片描述

sweepAngle取值范围是 [-360, 360),不包括360,当 >= 360 或者 < -360 时将不会绘制任何内容, 对于360,你可以用一个接近的值替代,例如: 359.99。

从名字就可以看出,这两个方法都是与圆弧相关的,作用都是添加一个圆弧到path中,但既然存在两个方法,两者之间肯定是有区别的:

这里写图片描述

可以看到addArc有1个方法(实际上是两个的,但另一个重载方法是API21添加的), 而arcTo有2个方法,其中一个最后多了一个布尔类型的变量forceMoveTo。

forceMoveTo是什么作用呢?

这个变量意思为“是否强制使用moveTo”,也就是说,是否使用moveTo将变量移动到圆弧的起点位移,也就意味着:

示例(addArc):

这里写图片描述

这里写图片描述

这里写图片描述

示例(arcTo):

这里写图片描述

这里写图片描述

第3组:isEmpty、 isRect、isConvex、 set 和 offset

isEmpty()
方法预览:

public boolean isEmpty ()

判断path中是否包含内容。

isRect()
方法预览:

public boolean isRect (RectF rect)

判断path是否是一个矩形,如果是一个矩形的话,会将矩形的信息存放进参数rect中。

set()
方法预览:

public void set (Path src)

将新的path赋值到现有path。

offset()
方法预览:

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

这个的作用也很简单,就是对path进行一段平移,它和Canvas中的translate作用很像,但Canvas作用于整个画布,而path的offset只作用于当前path。

但是第二个方法最后怎么会有一个path作为参数?

其实第二个方法中最后的参数das是存储平移后的path的。

这里写图片描述

这里写图片描述

从运行效果图可以看出,虽然我们在dst中添加了一个矩形,但是并没有表现出来,所以,当dst中存在内容时,dst中原有的内容会被清空,而存放平移后的path。