1. 程式人生 > >自定義元件開發二 Graphics API

自定義元件開發二 Graphics API

Graphics是Android SDK 中的一個包含一系列繪圖相關的api的包,本文介紹並使用常用的繪圖方法。

Point 類和 和 PointF

我們都知道在座標系中給定x/y兩個座標就可以確定一個點。
Point類就是表示一個點,他有兩個成員變數x、y代表點的 x 座標和 y 座標
圖形座標系與數學中的平面座標系有所不同,x 方向向左為負,向右為正,y 方向向上向負,向下為正,圖形座標系的原點在左上角
這裡寫圖片描述
預設情況下,當 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 軸的距離
這裡寫圖片描述

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

矩形的交集與並集運算
這裡寫圖片描述
傳入 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,
這裡寫圖片描述

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,也就是說,並集取的是四個方向的最大值。這裡寫圖片描述
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 都能獲得點陣圖的寬度和高度,對比如下:
這裡寫圖片描述

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 的大小與比例關係影響到最終的繪製效果。

這裡寫圖片描述

繪製 點

點的大小取決於 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 個角的弧度不能單獨設定,而是統一設定為相同的值。拐角弧度實際上是圓或橢圓的一段弧線

這裡寫圖片描述

繪製圓角矩形一共有 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)

該方法是上面方法的另一種過載形式。

繪製 圓

在這裡把圓、橢圓、扇形、弧線認為是圓或橢圓的一部分,放在一起。

橢圓的大小是由他的外切矩形來決定的

這裡寫圖片描述

繪製橢圓的方法如下:

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 為圓的半徑。

弧線和扇形本質上更是相似,弧線是橢圓上的一段,而扇形則是將弧線的兩個端點和橢圓中心點使用線條連線形成的閉合區域。理解弧線和扇形的演變過程便很容易明白方法中的引數意義。
這裡寫圖片描述

繪製弧線和扇形的方法如下:

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 表示弧線。

這裡寫圖片描述

繪製 路徑

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 類支援二階貝塞爾曲線和三階貝塞爾曲線。

這裡寫圖片描述

貝塞爾曲線通過 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 種圖形運算的不同效果:

這裡寫圖片描述
這裡寫圖片描述

繪製文字

繪製圖形也包括文字的繪製,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