Android绘图之Path(3)

Android 绘图学习

1 Path 简介

前面利用Paint,Canvas已经可以绘制出各式各样的简单图形了,本篇讲解Path意为路径,canvas中有drawPath函数按照路径绘制图形。利用Path可以更方便的添加节点,控制形状,对于复杂的形状简单的通过Canvas的绘制函数进行绘制繁琐且麻烦,使用Path可能会简化操作,一般复杂的图形都是用Path进行绘制。

注意: 关于网上流传的开启硬件加速对二维绘图(自定义控件绘制)的影响,这里不讨论,也不关闭硬件加速,硬件加速的确会影响绘制这时由于API中的某些函数不支持硬件加速(因为开启和不开启硬件加速走的绘制过程有所差异),所以需要关闭硬件加速才能起效果。
相关硬件加速请查阅:https://developer.android.com/guide/topics/graphics/hardware-accel

1.1 Path类的注释

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更能显示效果。

1.2 构造函数:

Path()
创建一个空的Path

Path(Path src)
利用已有Path生成新Path。

2 直线路径

Path用于绘制路径,所以如果是绘制点用不到Path,最简单的路径就是由直线组成,Path提供了简单的函数实现直线的绘制。
四个重要函数:

  • moveTo
  • lineTo
  • close
  • setLastPoint
  • rMoveTo
  • rLineTo

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);

未调用close函数

调用了close函数

多次调用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);

3 addArch 添加圆弧路径1

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);

4 arcTo 添加圆弧路径2

和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);

5 addCircle 添加圆形路径:

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);

6 addOval 添加椭圆路径

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);

7 addPath 添加Path路径

//合并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);

8 addRect addRoundRect 添加矩形

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);

9 quadTo cubicTo 曲线

quadTo ,cubicTo用来实现贝塞尔曲线,quadTo有一个控制点,cubicTo有两个控制点。
有点难度以后会开文章讲解。
mPath.moveTo(300,500);
mPath.quadTo(500,100,800,500);

贝塞尔曲线学习和使用

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

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 199,830评论 5 468
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 83,992评论 2 376
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 146,875评论 0 331
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 53,837评论 1 271
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 62,734评论 5 360
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,091评论 1 277
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,550评论 3 390
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,217评论 0 254
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,368评论 1 294
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,298评论 2 317
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,350评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,027评论 3 315
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,623评论 3 303
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,706评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,940评论 1 255
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,349评论 2 346
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 41,936评论 2 341

推荐阅读更多精彩内容