自定义组件开发二 Graphics API

时间:2022-06-13 11:00:46

Graphics是Android SDK 中的一个包含一系列绘图相关的api的包,本文介绍并使用常用的绘图方法。

Point 类和 和 PointF

我们都知道在坐标系中给定x/y两个坐标就可以确定一个点。
Point类就是表示一个点,他有两个成员变量x、y代表点的 x 坐标和 y 坐标
图形坐标系与数学中的平面坐标系有所不同,x 方向向左为负,向右为正,y 方向向上向负,向下为正,图形坐标系的原点在左上角
自定义组件开发二 Graphics API
默认情况下,当 x、y 为正数时该点会显示在屏幕之内(取决于屏幕大小),如果为负数就显示在屏幕之外。
Point 类提供的构造方法可以为 Point 提供一个(x,y)坐标值,也可以将一个 Point 的坐标值复制给另一个 Point。

public Point() {}
public Point(int x, int y) {
    this.x = x;
    this.y = y;
}
public Point(Point src) {
    this.x = src.x;
    this.y = src.y;
}

改变 x、y 的坐标值:Point 类提供了三个方法用于改变 Point 对象的 x、y 值,分别是:

public void set(int x, int y) {
    this.x = x;
    this.y = y;
}
public final void negate() {
    x = -x;
    y = -y;
}
public final void offset(int dx, int dy) {
    x += dx;
    y += dy;
}

set()方法简单粗暴地为 x、y 重新赋值,negate()方法将 x、y 取反,offset()方法则改变x、y 的偏移量,正负符号决定坐标偏移的方向。

PointF 类和 Point 类的定义是完全一样的,最大的不同就是成员变量 x、y 的类型不是 int 而是 float。PointF 提供了length()方法计算坐标原点(0,0)到(x,y)之间的距离,如下:

public final float length() {
    return length(x, y);
}
public static float length(float x, float y) {
    return FloatMath.sqrt(x * x + y * y);
}

Rect 类和 和 RectF 类

Rect 类表示矩形,其中有 left、top、right、bottom 四个成员变量。
left:矩形左边线条离 y 轴的距离
top:矩形上面线条离 x 轴的距离
right:矩形右边线条离 y 轴的距离
bottom:矩形底部线条离 x 轴的距离
自定义组件开发二 Graphics API

Rect 主要有两种初始化的方法:一是直接指定 left、top、right、bottom 等 4 个成员变量的值,二是从另一个 Rect 对象中复制。
下面是 Rect 的三个构造方法:
Rect()
Rect(int left, int top, int right, int bottom)
Rect(Rect r)

根据 left、top、right、bottom 等 4 个成员变量计算矩形的宽度、高度或中心点的坐标,主要的方法定义如下:
判断 Rect 是否为空,也就是矩形区域面积是否为 0 或者为无效矩形。

public final boolean isEmpty() {
    return left >= right || top >= bottom;
}

返回矩形的宽度/高度。

public final int width() {
    return right - left;
}

public final int height() {
    return bottom - top;
}

计算矩形中心点的 x 坐标、 y 坐标,右移一位相当于除以 2。

public final int centerX() {
    return (left + right) >> 1;
}

public final int centerY() {
    return (top + bottom) >> 1;
}

给 left、top、right 和 bottom 重新赋值,或者将另一个矩形 src复制成当前 矩形的 left、top、right 和 bottom 。

public void set(int left, int top, int right, int bottom) {
    this.left = left;
    this.top = top;
    this.right = right;
    this.bottom = bottom;
}

public void set(Rect src) {
    this.left = src.left;
    this.top = src.top;
    this.right = src.right;
    this.bottom = src.bottom;
}

矩形的交集与并集运算
自定义组件开发二 Graphics API
传入 Rect 的 left、top、right、bottom,并将构建的 Rect 对象与当前 Rect 对象做交集
运算,结果保存在当前 Rect 对象中。或者传入新的 Rect 对象,并将该对象与当前 Rect 对象做交集运算,结果保存在当前 Rect对象中。

public boolean intersect(int left, int top, int right, int bottom) {
    if (this.left < right && left < this.right && this.top < bottom && top < this.bottom) {
        if (this.left < left) this.left = left;
        if (this.top < top) this.top = top;
        if (this.right > right) this.right = right;
        if (this.bottom > bottom) this.bottom = bottom;
        return true;
    }
    return false;
}

public boolean intersect(Rect r) {
    return intersect(r.left, r.top, r.right, r.bottom);
}

比如有下面的代码段:

Rect rect1 = new Rect(0, 0, 400, 400);
Rect rect2 = new Rect(200, 200, 600, 600);
rect1.intersect(rect2);

此时,rect1 的 left、top、right、bottom 属性被改变了,分别为 200、200、400、400,
自定义组件开发二 Graphics API

union()方法是计算两个矩形的并集,传入一个新的 Rect,与当前 Rect 进行并集运算,并将结果保存在当前 Rect 对象中。

public void union(int left, int top, int right, int bottom) {
    if ((left < right) && (top < bottom)) {
        if ((this.left < this.right) && (this.top < this.bottom)) {
        if (this.left > left) this.left = left;
        if (this.top > top) this.top = top;
        if (this.right < right) this.right = right;
        if (this.bottom < bottom) this.bottom = bottom;
    } else {
        this.left = left;
        this.top = top;
        this.right = right;
        this.bottom = bottom;
        }
    }
}
public void union(Rect r) {
    union(r.left, r.top, r.right, r.bottom);
}

比如有下面的代码段:

Rect rect1 = new Rect(0, 0, 400, 400);
Rect rect2 = new Rect(200, 200, 600, 600);
rect1.union(rect2);

运行后与交集一样,最终的结果保存在 rect1 对象中, rect1 的 left、top、right、bottom属性值分别为:0,0,600,600,也就是说,并集取的是四个方向的最大值。自定义组件开发二 Graphics API
RectF类与 Rect 如出一辙,主要的不同是 Rect的 left、top、right、bottom 四个成员变量为 int 类型,而 RectF 为 float 类型。

Bitmap 类和 BitmapeDrawable 类

Bitmap 类是比较重要的一个类,Bitmap 译为“位图”,用于存储 png、jpg、gif 等格式的图片数据,很多时候如果需要在Android中对图片进行处理,需要先将图片读入 Bitmap 对象,接着调用相关的 API 对图片进行处理和加工,图片读取操作是由 BitmapFactory 类完成的,该类定义了若干方法用于读取图片数据:

public static Bitmap decodeStream(InputStream is)
从输入流中读取图片数据并转换成 Bitmap 对象
public static Bitmap decodeByteArray(byte[] data, int offset, int length)
从字节数组中读取图片数据并转换成 Bitmap 对象
public static Bitmap decodeResource(Resources res, int id)
从 Android 的 drawable 资源(res/drawable)目录中读取图片数据并转换成 Bitmap 对象
public static Bitmap decodeFile(String pathName)
从图片文件中读取图片数据并转换成 Bitmap 对象

我们也可以创建一张空白图片,空白图片需要指定宽度、高度和存储格式(ARGB_4444、
ARGB_8888、ALPHA_8)等信息。

下列的代码创建了一个 400*400 的 ARGB_8888 类型的空白位图对象: Bitmap bmp = Bitmap.createBitmap(400, 400, Config. ARGB_8888);

位于 res/drawable 目录下的图片读成 Bitmap 对象后是无法修改的,若要修改必须复制一张新的图片并设置可修改标记,Bitmap 类的 copy()方法能完成该功能,方法如下:

public Bitmap copy(Config config, boolean isMutable)
参数 isMutable 为 true 表示复制的新位图可以修改。

Bitmap 是一种非常占用资源的对象,不管是什么手机,如果没有处理好很容易导致 App 崩溃,所以,及时回收 Bitmap 内存是一个好习惯,涉及到的方法有两个:

public final boolean isRecycled()
判断是否已回收,返回 true 表示内存已被回收
public void recycle()
回收 Bitmap 内存,同一个 Bitmap 对象不能连续回收多次,所以在回收之前最好是先
判断。不过从源码中发现其实该方法已经自己判断过了。

常见的回收 Bitmap 资源的代码形如(bmp 为 Bitmap 对象):
if(bmp!= null && !bmp.isRecycled()){
bmp.recycle();
System.gc();//提醒 JVM 释放资源
bmp = null;

绘图中 Bitmap 是一个很重要的类,为了提高绘图的性能,通常会使用“双缓冲”技术,
什么是双缓冲技术?当数据量很大时,绘图可能需要几秒钟甚至更长的时间,而且有时还会出现闪烁现象,为了解决这些问题,可采用双缓冲技术来绘图。双缓冲即在内存中创建一个与屏幕绘图区域一致的对象,先将图形绘制到内存中的这个对象上,再一次性将这个对象上的图形拷贝到屏幕上,这样能大大加快绘图的速度。双缓冲实现过程如下:
  1、在内存中创建与画布一致的缓冲区
  2、在缓冲区画图
  3、将缓冲区位图拷贝到当前画布上
  4、释放内存缓冲区
双缓冲技术
https://www.jianshu.com/p/efc0bebfd22e?from=timeline

BimapDrawable 是 Android 的一种通用位图格式,我们可以简单粗暴地理解成 Bitmap 的另外一种表现形式。
但是和 Bitmap 相比 BimapDrawable 占用资源更少、性能更高。
Bitmap 和 BitmapDrawable 在一些情况下需要相互转换,BitmapDrawable 的构造方法publicBitmapDrawable(Resources res, Bitmap bitmap)用于将Bitmap转换成BitmapDrawable,而getBitmap()方法则用于将 BitmapDrawable 转换成 Bitmap。
Bitmap 和 BitmapDrawable 都能获得位图的宽度和高度,对比如下:
自定义组件开发二 Graphics API

Canvas 类与 Paint 类

画家作画时,有两样东西必不可少:画笔和宣纸,这里可以把Canvas 与 Paint 理解成画布和画笔。
Canvas 提供了若干方法用于绘制各种图形图案——点、线、圆等等。Paint 翻译为“画笔”,为绘图定义各种参数——颜色、线样式、图案样式等等。
通常的绘图思路是先定义 Paint 对象,指定绘图参数,再通过 Canvas 对象进行图形绘制,绘图的结果因 Paint 的不同而不同。

Paint 类

Paint 类用于定义绘图时的参数,主要包含颜色、文本、图形样式、位图模式、滤镜等几个方面。
颜色是指绘图时使用的颜色,在 Android 中颜色可以指定透明度,使用 16 进制来表示颜色时,格式通常为#AARRGGBB,其中,AA 表示透明度、RR 表示红色、GG 表示绿色、BB 表示蓝色,Color 类定义了颜色信息,内置了常用颜色的 int 型常量,比如 Color.RED 是红色,Color.BLUE 是蓝色……如果您习惯了 16 进制的颜色,Color 类的静态方法 parseColor(String colorString)可以将 16进制颜色转换成 Color 类型。需要注意的是,Android 的颜色都是 int 类型的,Color 类只负责颜色的管理而不是代表某种颜色。

下面的代码将 16 进制颜色转换成 Android 认可的颜色值:

int color = Color.parseColor("#66FF0000");

Paint 类中与颜色相关的方法有:

public native void setColor(int color);
设置颜色
public native void setAlpha(int a);
设置透明度,a 的范围取 0~255 之间的整数
public void setARGB(int a, int r, int g, int b)
指定透明度、红、绿、蓝定义一种颜色

绘制文本时,可以指定文本的大小、对齐方式、文本样式等属性,文本样式主要是为文本指定粗体、下划线、删除线等修饰性属性。
paint 类与文本相关的方法如下:

public native void setTextSize(float textSize)
设置文本大小,单位是 px,这个和我们平时使用的字体单位 sp 不同,所以最好进行转
换。
public void setTextAlign(Paint.Align align)
设置文本的对齐方式,可选值有 Paint.Align.LEFT、Paint.Align.CENTER、Paint.Align.RIGHT
等。
public native void setTextSkewX(float skewx)
设置文本的倾斜程度,skewx 取值于 0~1 之间,正负表示倾斜的方向。
public native void setUnderlineText(boolean underline)
给文本添加下载线,underline 为 true 表示添加。
public native void setFakeBoldText(boolean bold)
设置文本的粗体样式,bold 为 true 表示粗体。
public native void setStrikeThruText(boolean strike)
strike 为 true 时为文本添加删除线。

图形样式包含绘制的图形是空心样式还是实心样式,同时还能指定落笔和收笔时的笔触效
果。绘制直线或折线时,图形样式能影响到一些绘制细节。
Paint 类与图形样式相关的方法有:

public void setStyle(Paint.Style style)
设置绘制的图形是空心样式还是实心样式,默认为实心样式。style 的可选值有:
public static enum Style {
    FILL
    FILL_AND_STROKE,
    STROKE
}

其中,FILL 表示实心样式,对于闭合图形来说,会用指定的颜色进行填充;STROKE 表示空心样式,绘制时只有线条而无填充效果;FILL_AND_STROKE 表示同时使用实心样式和空心样式。
public void setStrokeJoin(Paint.Join join)
当绘图样式为 STROKE 时,该方法用于指定线条连接处的拐角样式,能使绘制的图形更加平滑。可选值如下:
public static enum Join {
    BEVEL,
    MITER,
    ROUND
}
public native void setStrokeWidth(float width)
设置线条的宽度,注意是 float 类型,在 Android 中最细的线条不是 1,可以比 1 更小更细。

Canvas 类

Canvas 类封装了大量的绘图方法,一般情况下,在绘图之前,要先创建 Paint 对象,定义绘制的颜色、样式。Paint 对象是一个轻量级对象,在程序运行过程中可以创建多个,我们也可以从头到尾只创建一个,最终取决于绘图需求。
不过,Paint 类有一个 reset()方法,能重置 Paint 参数,所以,除非有必要,我们并不推荐大量创建Paint 对象,而是调用 reset()方法重置后重复使用,不然这对有限的手机资源来说是一种考验。

Canvas 类定义的绘图方法主要分成:位图、点、线、矩形、圆、路径、文字 这几种类型。
下面分别介绍这几种类型

绘制 位图

一般我们绘图,现将图绘制在一个空的位图上,然后再将位图展示在ImageView上。
Canvas 中定义的绘制位图的方法有:

public void drawBitmap(Bitmap bitmap, float left, float top, Paint paint)

该方法是最为简单,将 bitmap 绘制在画布上,同时指定位图左上角相对于画布的坐标,大小与原位置相同,不进行任何缩放。绘制位图时,除非需要进行位图运算,否则,并不需要指定 paint 对象,直接传递null 即可。

public void drawBitmap(Bitmap bitmap, Rect src, Rect dst, Paint paint)
public void drawBitmap(Bitmap bitmap, Rect src, RectF dst, Paint paint)

这两个方法从 bitmap 中抠出一块大小区域为 src 的图片并绘制到 canvas 的 dst 处。src和 dst 的大小与比例关系影响到最终的绘制效果。

自定义组件开发二 Graphics API

绘制 点

点的大小取决于 setStrokeWidth()方法的参数,参数值越大,点也就越大。所以,不要以为一个点就是屏幕上的一个像素。如果将 stroke 的宽度设置为足够大,我们发现最终绘制出来的点其实是一个正方形。
绘制点的方法一共有三个:

public void drawPoint(float x, float y, Paint paint)
该方法在(x,y)处绘制一个点。

public void drawPoints(float[] pts, Paint paint)
该方法的参数 pts 是一个数组,从下标 0 开始每 2 个数确定一个点,连续绘制多个点。多余的元素会忽略。

public void drawPoints(float[] pts, int offset, int count, Paint paint)
从 pts 数组中的第 offset 处开始取出 count 个数字,以 2 个数为一组确实一个点,连续绘制若干个点。忽略多余的元素。

绘制 线

两个点确定一条直线,所以,绘制线条时,需要指定两个点的坐标。同画点一样,绘制线条也有 3 个重载的方法:

public void drawLine(float startX, float startY, float stopX, float stopY, Paint paint)
在(startX, startY)和(stopX, stopY)两个点之间绘制一条直线。

public void drawLines(float[] pts, Paint paint)
pts 数组中每 4 个数一组绘制一条直线,多余的元素会忽略。

public void drawLines(float[] pts, int offset, int count, Paint paint)
从 pts 数组中的 offset 索引处开始,取出 count 个元素,并以 4 个数一组绘制直线,忽略多余的元素。

绘制 矩形

矩形包含直角矩形和圆角矩形,正方形也是矩形的一种,所以 Canvas 并没有提供绘制正方形的方法。绘制矩形时,参数分为两种:一种是指定 left、top、right、bottom 等 4 个参数,另一种直接指定一个 Rect 对象或 RectF 对象。

绘制直角矩形的三个重载方法如下:

本质上这三个绘制直角矩形的方法是完全一样的,只是提供了调用的多种形态,使用哪一个完全由开发者自己决定。

public void drawRect(float left, float top, float right, float bottom, Paint paint)
public void drawRect(Rect r, Paint paint)
public void drawRect(RectF r, Paint paint)

圆角矩形的几何形状比直角矩形相对复杂一些,我们需要指定 4 个拐角的弧度,4 个角的弧度不能单独设置,而是统一设置为相同的值。拐角弧度实际上是圆或椭圆的一段弧线

自定义组件开发二 Graphics API

绘制圆角矩形一共有 2 个重载的方法:

public void drawRoundRect(float left, float top, float right, float bottom, float rx, float ry,Paint paint)

该方法用于绘制一个圆角矩形,left、top、right、bottom 构建一个矩形,rx、ry 分别是圆角处的水平半径和垂直半径。rx 和 ry 不一定相同,如果不同,则是椭圆上的一段弧线。

public void drawRoundRect(RectF rect, float rx, float ry, Paint paint)

该方法是上面方法的另一种重载形式。

绘制 圆

在这里把圆、椭圆、扇形、弧线认为是圆或椭圆的一部分,放在一起。

椭圆的大小是由他的外切矩形来决定的

自定义组件开发二 Graphics API

绘制椭圆的方法如下:

public void drawOval(float left, float top, float right, float bottom, Paint paint)
public void drawOval(RectF oval, Paint paint)

上面两个方法的参数定义了一个矩形结构,绘制的结果是该矩形的内切椭圆。

绘制椭圆时,如果外切矩形的长和宽相等,即为正方形,绘制出来的图形就是一个圆,但是 Cavnas 类提供了另一个更加简单实用的方法,提供圆点的坐标和半径即可。

public void drawCircle(float cx, float cy, float radius, Paint paint)

(cx、cy)为圆心坐标,radius 为圆的半径。

弧线和扇形本质上更是相似,弧线是椭圆上的一段,而扇形则是将弧线的两个端点和椭圆中心点使用线条连接形成的闭合区域。理解弧线和扇形的演变过程便很容易明白方法中的参数意义。
自定义组件开发二 Graphics API

绘制弧线和扇形的方法如下:

public void drawArc(RectF oval, float startAngle, float sweepAngle, boolean useCenter, Paintpaint)
public void drawArc(float left, float top, float right, float bottom, float startAngle, floatsweepAngle, boolean useCenter, Paint paint)

以上两个方法中,参数 startAngle 表示起始角度,sweepAngle 表示扇形或弧线所占的角度,正数表示顺时针,负数表示逆时针。useCenter 参数询问是否要使用中心点,为true 表示扇形,为 false 表示弧线。

自定义组件开发二 Graphics API

绘制 路径

Path 是 Graphics 中一个非常重要的概念,表示“路径”,路径可以是直的、也可以是弯的,可以是闭合的、也可以是非闭合的,可以是圆形的、也可以是方形的,可以是单个的、也可以是多个的,可以是简单的、也可以是复杂的……总的来说,路径是基于普通图形但是功能比普通图形更强的一种复杂图形。

Path 是一个类,用于绘制复杂图形,创建之初什么也没有,只有往 Path 中添加了具体的形状,Path 才会清晰可见。绘制 Path 时,所有信息都存储在 Path 对象中,Canvas 根据 Path 对象来绘制相应的图形。

我们将 Path 的功能归纳成以下几类:

往 Path 中添加线条
往 Path 中添加矩形、椭圆、弧
往 Path 中添加曲线和贝塞尔曲线
将 Path 中的图形进行运算

通过 Path 可以绘制出奇形怪状的线条,并能将线条组合在一起变成折线,闭合后就是一个多边形了。这就是 Path 的厉害之处。为此,Path 类中定义了 5 个方法:

public void moveTo(float x, float y)
将画笔移动到点(x,y)的位置,使用的是绝对定位。

public void rMoveTo(float dx, float dy)
将画笔移动到一个新点,新点在上一个点的基础上偏移(dx,dy),也就是说,新点的坐标为(x + dx,y + dy)。这里使用的是相对定位。首字母“r”就是“relative(相对)”的意思。

public void lineTo(float x, float y)
将画笔移动到点(x,y)的位置,并在上一个点与当前点之前画一条直线。使用的是绝对定位。

public void rLineTo(float dx, float dy)
将画笔移动到一个新点,新点在上一个点的基础上偏移(dx,dy),新点的坐标为(x +dx,y + dy),同时,在新点与上一个点之间画一条直线。这里使用的是相对定位。

public void close()
在第一个点和最后一个点之前画一条直线,形成闭合区域。

如果要往 Path 对象中添加矩形、椭圆、圆和弧,需要调用 Path 类中定义的一组以“add”开头的方法,这组方法有些需要传递一个类型为 Path.Direction 的参数,这是一个枚举类型,枚举值 CW 表示顺时针,CCW 表示逆时针

public void addRect(RectF rect, Path.Direction dir)
public void addRect(float left, float top, float right, float bottom, Path.Direction dir)
往 Path 对象中添加一个矩形。

public void addRoundRect(RectF rect, float[] radii, Path.Direction dir)
public void addRoundRect(RectF rect, float rx, float ry, Path.Direction dir)
public void addRoundRect(float left, float top, float right, float bottom, float[] radii,Path.Direction dir)
往 Path 对象中添加一个圆角矩形。该方法和前面绘制圆角矩形相比在定义四个角的弧
线大小时功能更强,能对四个角分别定义不同的弧线弧度。

public void addOval(RectF oval, Path.Direction dir)
public void addOval(float left, float top, float right, float bottom, Path.Direction dir)
往 Path 对象中添加一个椭圆。

public void addCircle(float x, float y, float radius, Path.Direction dir)
往 Path 对象中添加一个圆。

public void addArc(RectF oval, float startAngle, float sweepAngle)
public void addArc(float left, float top, float right, float bottom, float startAngle, float sweepAngle)
往 Path 对象中添加一段弧。本方法并没有指定方向,因为角度的正负已经代表了方向,正数为顺时针,负数为逆时针。

曲线包括弧线和贝塞尔曲线,与前面讲的矩形、圆或弧线不同,绘制曲线时需要确定一个起点,绘制的曲线会与该起点进行连接,形成一个更加复杂的图形。

贝塞尔曲线(Bézier curve)是图形开发中的一个重要工具,通过三个点的,确定一条平滑的曲线,又称贝兹曲线或贝济埃曲线,是应用于二维图形应用程序的数学曲线。一般的矢量图形软件通过它来精确画出曲线,贝兹曲线由线段与节点组成,节点是可拖动的支点,线段像可伸缩的皮筋,我们在绘图工具上看到的钢笔工具就是来做这种矢量曲线的。贝塞尔曲线是计算机图形学中相当重要的参数曲线,在一些比较成熟的位图软件中也有贝塞尔曲线工具。

贝塞尔曲线又分为一阶贝塞尔曲线、二阶贝塞尔曲线、三阶贝塞尔曲线和高阶贝塞尔曲线,一阶贝塞尔曲线就是一条线段,Path 类支持二阶贝塞尔曲线和三阶贝塞尔曲线。

自定义组件开发二 Graphics API

贝塞尔曲线通过 3 个点来绘制一条平滑的曲线,这 3 个点分别是起点、控制点和终点。比如,如果要绘制一条二阶贝塞尔曲线,必须调用 moveTo()方法定义起点,再调用 public void quadTo(float x1, float y1, float x2, float y2)方法绘制贝塞尔曲线,其中,(x1,y1)是控制点,(x2,y2)是终点。

三阶贝塞尔曲线有 1 个起点,2 个控制点,1 个终点,Path 类中通过 public void cubicTo(floatx1, float y1, float x2, float y2, float x3, float y3)进行绘制,其中,(x1、y1)、(x2、y2)是控制点,(x3、y3)是终点。

quadTo()和 cubicTo()的控制点和终点利用绝对定位来进行确定,其实还有另外两个方法,通过相对定位对各点进行定义:
public void rQuadTo(float dx1, float dy1, float dx2, float dy2)
public void rCubicTo(float x1, float y1, float x2, float y2, float x3, float y3)

arcTo()方法可以和 moveTo()配合使用,通过 moveTo()确定一个起点,再通过 arcTo()绘制弧线。弧线是基于矩形的内切圆上的一段,该弧线的起始点会和 moveTo()方法定义的点进行连接。
public void arcTo(RectF oval, float startAngle, float sweepAngle, boolean forceMoveTo)
public void arcTo(RectF oval, float startAngle, float sweepAngle)
public void arcTo(float left, float top, float right, float bottom, float startAngle, float
sweepAngle, boolean forceMoveTo)

参数 forceMoveTo 为 true 时,表示开始一个新的图形,不和上一个点进行连接,为 false 时才和上一个点连接。

还可以将多个 Path 进行图形运算,得到更加复杂和不规则的图形。Path 有一个静态内
部类 Op,定义了 5 种运算规则:

Path.Op. DIFFERENCE
差集,图形 A 减去与图形 B 重叠的区域后 A 余下的区域。

Path.Op. INTERSECT
交集,图形 A 和图形 B 的重叠区域。

Path.Op. REVERSE_DIFFERENCE
反差集,图形 B 减去与图形 A 重叠的区域后 B 余下的区域。

Path.Op. UNION
并集,包含了图形 A 和图形 B 的所有区域。

Path.Op.XOR
补集,即图形 A 和图形 B 的所有区域减去他们的重叠区域后余下的区域。

以下的表格来比较这 5 种图形运算的不同效果:

自定义组件开发二 Graphics API
自定义组件开发二 Graphics API

绘制文字

绘制图形也包括文字的绘制,Canvas为我们提供了两组方法,一组直接从指定的位置开始绘制文字,另一组沿着 Path 绘制文字:

 public void drawText(char[] text, int index, int count, float x, float y, Paint paint)
 public void drawText(String text, float x, float y, Paint paint)
 public void drawText(String text, int start, int end, float x, float y, Paint paint)
 public void drawText(CharSequence text, int start, int end, float x, float y, Paint paint)

上面这一组方法是从指定的位置(坐标)开始绘制文字,虽然都是字符串,但是提供了三种形式:char[]、String 和 CharSequence,本质上并没有什么不同,参数 index 和count、start 和 end 可以从字符串中取出子串,而参数 x、y 就是文字绘制的坐标位置,其中 y 是文字的 baseline 的值(具体请参考 6.3 小节案例)。

public void drawTextOnPath(String text, Path path, float hOffset, float vOffset, Paint paint)
public void drawTextOnPath(char[] text, int index, int count, Path path, float hOffset, float
vOffset, Paint paint)

这两个重载的 drawTextOnPath()方法用于沿着 Path 定义好的路径绘制文字,这是一个很在趣的功能,文字在 Path 的带领下龙飞凤舞,灵活多变。参数 hOffset 和 vOffset 用于定义文字离 Path 的水平偏移量和垂直偏移量,正数和负数影响文字与路径的相对位置。同样的,也支持绘制从字符数组中截取的子串,index 表示起始索引,count 表示要截取的长度。

文中涉及的API的使用可参看Demo

谢谢认真观读本文的每一位小伙伴,衷心欢迎小伙伴给我指出文中的错误,也欢迎小伙伴与我交流学习。
欢迎爱学习的小伙伴加群一起进步:230274309