android绘图之Paint(1)
android绘图之Canvas基础(2)
Android绘图之Path(3)
Android绘图之drawText绘制文本相关(4)
Android绘图之Canvas概念理解(5)
Android绘图之Canvas变换(6)
Android绘图之Canvas状态保存和恢复(7)
Android绘图之PathEffect (8)
Android绘图之LinearGradient线性渐变(9)
Android绘图之SweepGradient(10)
Android绘图之RadialGradient 放射渐变(11)
Android绘制之BitmapShader(12)
Android绘图之ComposeShader,PorterDuff.mode及Xfermode(13)
Android绘图之drawText,getTextBounds,measureText,FontMetrics,基线(14)
Android绘图之贝塞尔曲线简介(15)
Android绘图之PathMeasure(16)
Android 动态修改渐变 GradientDrawable
前面利用Paint,Canvas已经可以绘制出各式各样的简单图形了,本篇讲解Path意为路径,canvas中有drawPath函数按照路径绘制图形。利用Path可以更方便的添加节点,控制形状,对于复杂的形状简单的通过Canvas的绘制函数进行绘制繁琐且麻烦,使用Path可能会简化操作,一般复杂的图形都是用Path进行绘制。
注意: 关于网上流传的开启硬件加速对二维绘图(自定义控件绘制)的影响,这里不讨论,也不关闭硬件加速,硬件加速的确会影响绘制这时由于API中的某些函数不支持硬件加速(因为开启和不开启硬件加速走的绘制过程有所差异),所以需要关闭硬件加速才能起效果。
相关硬件加速请查阅:https://developer.android.com/guide/topics/graphics/hardware-accel
The Path class encapsulates compound (multiple contour) geometric paths
consisting of straight line segments, quadratic curves, and cubic curves.
It can be drawn with canvas.drawPath(path, paint), either filled or stroked
(based on the paint’s Style), or it can be used for clipping or to draw
text on a path.
Path可用于绘制直线,曲线构成几何路径,还可以用于剪裁画布和根据路径绘制文字。
Paint的绘制模式设置成stroke更能显示效果。
Path()
创建一个空的Path
Path(Path src)
利用已有Path生成新Path。
Path用于绘制路径,所以如果是绘制点用不到Path,最简单的路径就是由直线组成,Path提供了简单的函数实现直线的绘制。
四个重要函数:
moveTo
public void moveTo(float x, float y) ;
moveTo (float x, float y):直线的开始点(也可能是下一个形状的开始点)即将直线路径的绘制位置定在(x,y)的位置;
rMoveTo在前一个点的基础上开始绘制新的直线。
lineTo
public void lineTo(float x, float y) ;
void lineTo (float x2, float y2):结束点或者下一次绘制直线路径的开始点;
rLineto:在前面路径的基础上连续绘制,如果前面一个点是(x,y),rLineTo(x1,y1)相当于lineTo(x+x1,y+y1),如果前面没有调用moveTo,相当于从(0,0)开始绘制。
多次调用lineTo可以一直调用。如果一个直线开始绘制没有调用moveTo,默认第一个点从(0,0)点开始绘制。lineTo添加的是直线而不仅仅是点。
public void close() ;
void close ():如果绘制的直线没有形成闭环,调用Close()会将路径首尾点连接起来,形成闭环;
/**
* Sets the last point of the path.
*
* @param dx The new X coordinate for the last point
* @param dy The new Y coordinate for the last point
*/
public void setLastPoint(float dx, float dy) {
isSimplePath = false;
nSetLastPoint(mNativePath, dx, dy);
}
setLastPoint 会重新设置最后一个点的坐标。
mPath = new Path();
mPath.moveTo(100,70);
mPath.lineTo(140,180);
mPath.lineTo(250,330);
mPath.lineTo(400,630);
mPath.lineTo(100,830);
多次调用moveTo可以绘制多条不相关的线段:
mPath.moveTo(100,70);
mPath.lineTo(140,180);
mPath.lineTo(250,330);
mPath.lineTo(400,630);
mPath.lineTo(100,830);
mPath.moveTo(100,1000);
mPath.lineTo(600,1300);
mPath.lineTo(400,1700);
setLastPoint重置最后一个点的坐标。
mPath.moveTo(100,70);
mPath.lineTo(140,180);
mPath.lineTo(250,330);
mPath.lineTo(400,630);
mPath.lineTo(100,830);
mPath.moveTo(100,1000);
mPath.lineTo(600,1300);
mPath.lineTo(400,1700);
mPath.setLastPoint(300,1700);
addArc
public void addArc(RectF oval, float startAngle, float sweepAngle) {
addArc(oval.left, oval.top, oval.right, oval.bottom, startAngle, sweepAngle);
}
//利用坐标点代替RectF
public void addArc(float left, float top, float right, float bottom, float startAngle,
float sweepAngle) {
isSimplePath = false;
nAddArc(mNativePath, left, top, right, bottom, startAngle, sweepAngle);
}
添加圆弧路径:
oval:矩形区域
startAngle:开始弧度
sweepAngle:弧形扫过的弧度
x轴正方向为0度,然后顺时针绘制。
实例代码
RectF rectF1 = new RectF(200,200,500,500);
RectF rectF2 = new RectF(200,600,500,900);
mPath.addArc(rectF1,180,180);
mPath.addArc(rectF2,0,270);
和addArc类似,添加一个圆弧到path,如果圆弧的起点和上次最后一个坐标点不相同,就连接两个点。
public void arcTo(RectF oval, float startAngle, float sweepAngle,
boolean forceMoveTo) {
arcTo(oval.left, oval.top, oval.right, oval.bottom, startAngle, sweepAngle, forceMoveTo);
}
public void arcTo(RectF oval, float startAngle, float sweepAngle) {
arcTo(oval.left, oval.top, oval.right, oval.bottom, startAngle, sweepAngle, false);
}
public void arcTo(float left, float top, float right, float bottom, float startAngle,
float sweepAngle, boolean forceMoveTo) {
isSimplePath = false;
nArcTo(mNativePath, left, top, right, bottom, startAngle, sweepAngle, forceMoveTo);
}
参数:forceMoveTo
true 将最后一个点移动到圆弧起点,即不连接最后一个点与圆弧起点,
false 不移动,而是连接最后一个点与圆弧起点
示例代码:
mPath.moveTo(0,0);
mPath.lineTo(100,100);
mPath.arcTo(rectF1,0,270,true);
mPath.moveTo(0,0);
mPath.lineTo(100,100);
mPath.arcTo(rectF1,0,270,false);
public void addCircle(float x, float y, float radius, Direction dir) {
isSimplePath = false;
nAddCircle(mNativePath, x, y, radius, dir.nativeInt);
}
x,y:圆心坐标
Radius:半径
Direction:方向
Path.Direction:设置添加path的顺序,CW顺时针,CCW逆时针,在添加图形时确定闭合顺序(各个点的记录顺序)
public enum Direction {
/** clockwise */
CW (0), // must match enum in SkPath.h
/** counter-clockwise */
CCW (1); // must match enum in SkPath.h
}
所以Direction会改变连接顺序,类似绘制矩形A-B-C-D ,顺时针绘制顺序为A-B-C-D,如果设置了逆时针就是A-D-C-B。
注意:绘制圆形,矩形最终图形不受顺序影响,按照路径绘制文字,会按照Direction方向进行绘制。
mPath.addCircle(400,400,300, Path.Direction.CW);
public void addOval(RectF oval, Direction dir) {
addOval(oval.left, oval.top, oval.right, oval.bottom, dir);
}
public void addOval(float left, float top, float right, float bottom, Direction dir) {
isSimplePath = false;
nAddOval(mNativePath, left, top, right, bottom, dir.nativeInt);
}
RectF rectF1 = new RectF(200,200,500,600);
mPath.addOval(rectF1,Path.Direction.CW);
//合并path之前做偏移处理
public void addPath(Path src, float dx, float dy) {
isSimplePath = false;
nAddPath(mNativePath, src.mNativePath, dx, dy);
}
//合并两个Path
public void addPath(Path src) {
isSimplePath = false;
nAddPath(mNativePath, src.mNativePath);
}
//添加路径之前做matrix变换
public void addPath(Path src, Matrix matrix) {
if (!src.isSimplePath) isSimplePath = false;
nAddPath(mNativePath, src.mNativePath, matrix.native_instance);
}
addPath(Path src, float dx, float dy)进行了位移之后再添加进当前path中。
addPath(Path)将两个Path合并成为一个。
addPath(Path src, Matrix matrix)添加到当前path之前先使用Matrix进行变换。
第一种
mPath.moveTo(100,70);
mPath.lineTo(140,180);
mPath.lineTo(250,330);
mPath.lineTo(400,630);
mPath.lineTo(100,830);
Path newPath = new Path();
newPath .moveTo(100,1000);
newPath .lineTo(600,1300);
newPath .lineTo(400,1700);
mPath.addPath(newPath);
第二种
mPath.moveTo(100,70);
mPath.lineTo(140,180);
mPath.lineTo(250,330);
mPath.lineTo(400,630);
mPath.lineTo(100,830);
Path newPath = new Path();
newPath.moveTo(100,1000);
newPath.lineTo(600,1300);
newPath.lineTo(400,1700);
mPath.addPath(newPath,300,100);
生成的图形向右向下移动了。
第三种
mPath.moveTo(100,70);
mPath.lineTo(140,180);
mPath.lineTo(250,330);
mPath.lineTo(400,630);
mPath.lineTo(100,830);
Path newPath = new Path();
newPath.moveTo(100,1000);
newPath.lineTo(600,1300);
newPath.lineTo(400,1700);
Matrix matrix = new Matrix();
matrix.postScale(0.5f,0.5f);
mPath.addPath(newPath,matrix);
public void addRect(RectF rect, Direction dir) {
addRect(rect.left, rect.top, rect.right, rect.bottom, dir);
}
public void addRect(float left, float top, float right, float bottom, Direction dir) {
detectSimplePath(left, top, right, bottom, dir);
nAddRect(mNativePath, left, top, right, bottom, dir.nativeInt);
}
public void addRoundRect(RectF rect, float rx, float ry, Direction dir) {
addRoundRect(rect.left, rect.top, rect.right, rect.bottom, rx, ry, dir);
}
public void addRoundRect(float left, float top, float right, float bottom, float rx, float ry,
Direction dir) {
isSimplePath = false;
nAddRoundRect(mNativePath, left, top, right, bottom, rx, ry, dir.nativeInt);
}
public void addRoundRect(RectF rect, float[] radii, Direction dir) {
if (rect == null) {
throw new NullPointerException("need rect parameter");
}
addRoundRect(rect.left, rect.top, rect.right, rect.bottom, radii, dir);
}
实例代码:
RectF rectF = new RectF(200, 200, 500, 700);
RectF rectF5 = new RectF(200, 800, 700, 1200);
mPath.addRect(rectF, Path.Direction.CCW);
mPath.addRoundRect(rectF5, 20,20,Path.Direction.CCW);
定制四个角的弧度
addRoundRect(RectF rect, float[] radii, Direction dir)可以定制矩形四个角的弧度。
float[] radii:需要传入8个数值,分四组,分别对应每个角所使用的椭圆的横轴半径和纵轴半径,从左上角开始。
RectF rectF3 = new RectF(200,200,900,700);
float[] radii = {70, 70, 70, 30, 30, 70, 70, 0};
mPath.addRoundRect(rectF3,radii, Path.Direction.CW);
quadTo ,cubicTo用来实现贝塞尔曲线,quadTo有一个控制点,cubicTo有两个控制点。
有点难度以后会开文章讲解。
mPath.moveTo(300,500);
mPath.quadTo(500,100,800,500);