這個程式碼研究室是「Android Kotlin 進階功能」課程的一部分。如果您按部就班完成每一堂程式碼研究室課程,就能充分體驗到本課程的價值,但這不是強制要求。如要查看所有課程程式碼研究室,請前往 Android Kotlin 進階功能程式碼研究室登陸頁面。
簡介
在本程式碼研究室中,裁剪是指定義圖片、畫布或點陣圖的區域,並選擇性地繪製或不繪製到畫面上。剪裁的用途之一是減少過度繪製。過度繪製是指為了顯示最終圖像,螢幕上的像素繪製超過一次。減少過度繪製可盡量減少繪製像素或顯示區域的次數,進而提升繪製效能。您也可以使用剪裁,在使用者介面設計和動畫中建立有趣的效果。
舉例來說,當您繪製一疊重疊的卡片 (如下所示) 時,與其從底部開始完整繪製每張卡片,通常只繪製可見部分會更有效率。之所以說「通常」,是因為裁剪作業也會產生費用,而且整體而言,Android 系統會進行許多繪圖最佳化作業。

如要只繪製資訊卡的可見部分,請為每張資訊卡指定裁剪區域。舉例來說,在下圖中,如果對圖片套用裁剪矩形,系統只會顯示矩形內的圖片部分。

裁剪區域通常是矩形,但可以是任何形狀或形狀組合,甚至是文字。您也可以指定要納入或排除剪裁區域內的區域。舉例來說,您可以建立圓形剪裁區域,只顯示圓圈外的內容。
在本程式碼研究室中,您將嘗試各種剪裁方式。
必備知識
您必須已經熟悉下列項目:
- 瞭解如何使用 Android Studio 建立及執行含有
Activity的應用程式。 - 如何建立及繪製
Canvas。 - 如何建立自訂
View,並覆寫onDraw()和onSizeChanged()。
課程內容
- 如何剪輯物件,以便在
Canvas上繪圖。 - 如何儲存及還原畫布的繪圖狀態。
- 如何對畫布和文字套用轉換。
學習內容
- 建立應用程式,在畫面上繪製經過裁剪的形狀,展示不同的裁剪方式,以及這些方式對形狀可見度的影響。
- 您也會繪製一些經過轉換和傾斜的文字。
ClippingExample 應用程式會示範如何使用及合併形狀,指定要在檢視區塊中顯示畫布的哪些部分。最終應用程式會如下方螢幕截圖所示。

您將從頭建構這個應用程式,因此必須設定專案、定義維度和字串,並宣告一些變數。
步驟 1:建立 ClippingExample 專案
- 使用「Empty Activity」範本建立名為
ClippingExample的 Kotlin 專案。套件名稱前置字串請使用com.example.android。 - 開啟
MainActivity.kt。 - 在
onCreate()方法中,取代預設內容檢視區塊,並將內容檢視區塊設為ClippedView的新例項。這就是您稍後要建立的剪輯範例自訂檢視畫面。
setContentView(ClippedView(this))- 在與
MainActivity.kt相同的層級,為名為ClippedView的自訂檢視區塊建立新的 Kotlin 檔案和類別,並擴充View。並提供下方顯示的簽章。其餘工作都會在這個ClippedView內完成。@JvmOverloads註解會指示 Kotlin 編譯器為這個函式產生多載,以取代預設參數值。
class ClippedView @JvmOverloads constructor(
context: Context,
attrs: AttributeSet? = null,
defStyleAttr: Int = 0
) : View(context, attrs, defStyleAttr) {
}步驟 2:新增維度和字串資源
- 在
res/values/dimens.xml的新資源檔案中,定義要用於裁剪檢視區塊的尺寸。這些預設尺寸經過硬式編碼,大小適合較小的螢幕。
<?xml version="1.0" encoding="utf-8"?>
<resources>
<dimen name="clipRectRight">90dp</dimen>
<dimen name="clipRectBottom">90dp</dimen>
<dimen name="clipRectTop">0dp</dimen>
<dimen name="clipRectLeft">0dp</dimen>
<dimen name="rectInset">8dp</dimen>
<dimen name="smallRectOffset">40dp</dimen>
<dimen name="circleRadius">30dp</dimen>
<dimen name="textOffset">20dp</dimen>
<dimen name="strokeWidth">4dp</dimen>
<dimen name="textSize">18sp</dimen>
</resources>如要讓應用程式在大螢幕上呈現良好效果 (並更輕鬆查看詳細資料),您可以建立 dimens 檔案,其中包含僅適用於大螢幕的較大值。
- 在 Android Studio 中,以滑鼠右鍵按一下「values」資料夾,然後選擇「New」>「Values resource file」。
- 在「New Resource File」對話方塊中,將檔案命名為
dimens。在「Available qualifiers」中選取「Smallest Screen Width」,然後按一下「>>」按鈕,將其新增至「Chosen qualifiers」。在「Smallest screen width」(最小螢幕寬度) 方塊中輸入 480,然後按一下「OK」(確定)。

- 檔案應會顯示在 values 資料夾中,如下所示。

- 如果找不到檔案,請切換至應用程式的「Project Files」(專案檔案) 檢視畫面。新檔案的完整路徑如下所示:
ClippingExample/app/src/main/res/values-sw480dp/dimens.xml。

- 將
values-sw480dp/dimens.xml檔案的預設內容替換為下列尺寸。
<?xml version="1.0" encoding="utf-8"?>
<resources>
<dimen name="clipRectRight">120dp</dimen>
<dimen name="clipRectBottom">120dp</dimen>
<dimen name="rectInset">10dp</dimen>
<dimen name="smallRectOffset">50dp</dimen>
<dimen name="circleRadius">40dp</dimen>
<dimen name="textOffset">25dp</dimen>
<dimen name="strokeWidth">6dp</dimen>
</resources>- 在
strings.xml中新增下列字串。這些屬性會用於在畫布上顯示文字。
<string name="clipping">Clipping</string>
<string name="translated">translated text</string>
<string name="skewed">"Skewed and "</string>步驟 3:建立並初始化 Paint 和 Path 物件
- 切換回專案的「Android」Android檢視畫面。
- 在
ClippedView中,定義要用於繪圖的Paint變數。啟用反鋸齒,並使用尺寸中定義的筆觸寬度和文字大小,如下所示。
private val paint = Paint().apply {
// Smooth out edges of what is drawn without affecting shape.
isAntiAlias = true
strokeWidth = resources.getDimension(R.dimen.strokeWidth)
textSize = resources.getDimension(R.dimen.textSize)
}- 在
ClippedView中,建立並初始化Path,在本機儲存繪製內容的路徑。匯入android.graphics.Path。
private val path = Path()步驟 4:設定形狀
在這個應用程式中,您會顯示多列和兩欄的形狀,並以各種方式裁剪。
這些服務的共通點:
- 做為容器的大矩形 (正方形)
- 大矩形上的對角線
- 圓圈
- 一小段文字

在這個步驟中,您要為資源中的形狀設定尺寸,這樣之後使用這些形狀時,只需要取得一次尺寸即可。
- 在
ClippedView的path下方,為整組形狀周圍的裁剪矩形新增維度變數。
private val clipRectRight = resources.getDimension(R.dimen.clipRectRight)
private val clipRectBottom = resources.getDimension(R.dimen.clipRectBottom)
private val clipRectTop = resources.getDimension(R.dimen.clipRectTop)
private val clipRectLeft = resources.getDimension(R.dimen.clipRectLeft)- 新增矩形插邊和小型矩形偏移的變數。
private val rectInset = resources.getDimension(R.dimen.rectInset)
private val smallRectOffset = resources.getDimension(R.dimen.smallRectOffset)- 新增圓形半徑的變數,這是矩形內繪製的圓形半徑。
private val circleRadius = resources.getDimension(R.dimen.circleRadius)- 為矩形內繪製的文字新增位移和文字大小。
private val textOffset = resources.getDimension(R.dimen.textOffset)
private val textSize = resources.getDimension(R.dimen.textSize)步驟 4:設定列和欄的位置
這個應用程式的形狀會顯示在兩欄四列中,取決於上方設定的維度值。這項計算的數學概念不屬於本程式碼研究室的範圍,但請在複製這個步驟提供的程式碼時查看。
- 設定兩欄的座標。
private val columnOne = rectInset
private val columnTwo = columnOne + rectInset + clipRectRight- 為每個資料列新增座標,包括轉換後文字的最後一個資料列。
private val rowOne = rectInset
private val rowTwo = rowOne + rectInset + clipRectBottom
private val rowThree = rowTwo + rectInset + clipRectBottom
private val rowFour = rowThree + rectInset + clipRectBottom
private val textRow = rowFour + (1.5f * clipRectBottom)- 執行應用程式。應用程式應會開啟,並在應用程式名稱下方顯示空白白色畫面。

在 onDraw() 中,您會呼叫方法來繪製七個不同的剪裁矩形,如下方應用程式螢幕截圖所示。所有矩形都是以相同方式繪製,唯一的差異是定義的裁剪區域和螢幕上的位置。

繪製矩形的演算法運作方式如下圖和說明所示。總而言之,您要移動 Canvas 的原點,繪製一系列矩形。概念上,這包含下列步驟:

(1) 首先,將 Canvas 翻譯成要繪製矩形的位置。也就是說,您不必計算下一個矩形和所有其他形狀的繪製位置,而是移動Canvas原點,也就是座標系統。
(2) 接著,在畫布的新原點繪製矩形。也就是說,您在翻譯後的座標系統中,於相同位置繪製形狀。這樣做簡單許多,效率也稍微提升。
(3) 最後,將 Canvas 還原為原始的 Origin。
以下是您要實作的演算法:
- 在
onDraw()中呼叫函式,以灰色背景顏色填滿Canvas,並繪製原始形狀。 - 針對每個剪裁矩形和要繪製的文字呼叫函式。
針對每個矩形或文字執行下列操作:
- 儲存
Canvas的目前狀態,以便重設為初始狀態。 - 將畫布的
Origin轉譯到要繪製的位置。 - 套用剪裁形狀和路徑。
- 繪製矩形或文字。
- 還原
Canvas的狀態。
步驟:覆寫 onDraw()
- 覆寫
onDraw(),如下列程式碼所示。您要為繪製的每個形狀呼叫函式,稍後會實作這些函式。
override fun onDraw(canvas: Canvas) {
super.onDraw(canvas)
drawBackAndUnclippedRectangle(canvas)
drawDifferenceClippingExample(canvas)
drawCircularClippingExample(canvas)
drawIntersectionClippingExample(canvas)
drawCombinedClippingExample(canvas)
drawRoundedRectangleClippingExample(canvas)
drawOutsideClippingExample(canvas)
drawSkewedTextExample(canvas)
drawTranslatedTextExample(canvas)
// drawQuickRejectExample(canvas)
}- 為每個繪圖函式建立存根,讓程式碼繼續編譯。您可以複製下方的程式碼。
private fun drawBackAndUnclippedRectangle(canvas: Canvas){
}
private fun drawDifferenceClippingExample(canvas: Canvas){
}
private fun drawCircularClippingExample(canvas: Canvas){
}
private fun drawIntersectionClippingExample(canvas: Canvas){
}
private fun drawCombinedClippingExample(canvas: Canvas){
}
private fun drawRoundedRectangleClippingExample(canvas: Canvas){
}
private fun drawOutsideClippingExample(canvas: Canvas){
}
private fun drawTranslatedTextExample(canvas: Canvas){
}
private fun drawSkewedTextExample(canvas: Canvas){
}
private fun drawQuickRejectExample(canvas: Canvas){
}應用程式會繪製相同的矩形和形狀七次,第一次不使用裁剪,之後六次則套用各種裁剪路徑。drawClippedRectangle() 方法會將繪製一個矩形的程式碼分解出來,如下所示。

步驟 1:建立 drawClippedRectangle() 方法
- 建立
drawClippedRectangle()方法,並將Canvas類型的引數canvas傳遞至該方法。
private fun drawClippedRectangle(canvas: Canvas) {
}- 在
drawClippedRectangle()方法中,為整個形狀設定剪裁矩形的界線。套用裁剪矩形,將繪圖限制為僅限正方形。
canvas.clipRect(
clipRectLeft,clipRectTop,
clipRectRight,clipRectBottom
)Canvas.clipRect(...) 方法會縮減螢幕區域,讓未來的繪圖作業可寫入該區域。這會將裁剪邊界設為目前裁剪矩形與傳遞至 clipRect() 的矩形之間的空間交集。clipRect() 方法有多種變體,可接受不同形式的區域,並允許對裁剪矩形執行不同作業。
- 將
canvas填滿白色。當然可以!整個畫布,因為您不是繪製矩形,而是剪裁!由於有剪裁矩形,因此只有剪裁矩形定義的區域會填滿,形成白色矩形。其餘表面則維持灰色。
canvas.drawColor(Color.WHITE)- 將顏色變更為紅色,並在剪裁矩形內繪製對角線。
paint.color = Color.RED
canvas.drawLine(
clipRectLeft,clipRectTop,
clipRectRight,clipRectBottom,paint
)- 將顏色設為綠色,並在裁剪矩形內繪製圓形。
paint.color = Color.GREEN
canvas.drawCircle(
circleRadius,clipRectBottom - circleRadius,
circleRadius,paint
)- 將顏色設為藍色,並繪製與裁剪矩形右側邊緣對齊的文字。使用
canvas.drawText()繪製文字。
paint.color = Color.BLUE
// Align the RIGHT side of the text with the origin.
paint.textSize = textSize
paint.textAlign = Paint.Align.RIGHT
canvas.drawText(
context.getString(R.string.clipping),
clipRectRight,textOffset,paint
)步驟 2:實作 drawBackAndUnclippedRectangle() 方法
- 如要查看
drawClippedRectangle()方法的實際運作情形,請實作drawBackAndUnclippedRectangle()方法,繪製第一個未裁剪的矩形,如下所示。儲存canvas、平移至第一列和第一欄的位置、呼叫drawClippedRectangle()繪製,然後將canvas還原至先前的狀態。
private fun drawBackAndUnclippedRectangle(canvas: Canvas){
canvas.drawColor(Color.GRAY)
canvas.save()
canvas.translate(columnOne,rowOne)
drawClippedRectangle(canvas)
canvas.restore()
}- 執行應用程式。您應該會看到第一個白色矩形,其中包含圓圈、紅線和文字,背景為灰色。

在下列剪裁範例方法中,您會套用各種剪裁區域組合來達成圖形效果,並瞭解如何組合剪裁區域來建立所需形狀。
這些方法都遵循相同的模式。
- 儲存畫布的目前狀態:
canvas.save()
活動內容會維護繪圖狀態堆疊。繪圖狀態包含目前的轉換矩陣和目前的剪裁區域。您可以儲存目前狀態、執行會變更繪圖狀態的動作 (例如平移或旋轉畫布),然後還原儲存的繪圖狀態。(注意:這類似於 Git 中的「stash」指令!)。
如果繪圖包含轉換,則透過反向轉換來鏈結及復原轉換時,很容易發生錯誤。舉例來說,如果先平移、再延展,然後旋轉,很快就會變得複雜。請改為儲存畫布的狀態、套用轉換、繪製,然後還原先前的狀態。
舉例來說,您可以定義裁剪區域,並儲存該狀態。然後翻譯畫布、新增剪裁區域及旋轉。繪製一些內容後,您可以還原原始剪裁狀態,然後繼續進行不同的平移和傾斜轉換,如圖所示。

- 將畫布原點轉換為列/欄座標:
canvas.translate()
相較於移動所有要繪製的元素,移動畫布原點並在新座標系統中繪製相同內容簡單許多。(提示:您可以使用相同技巧旋轉元素)。
- 如有需要,請對
path套用轉換。 - 套用剪輯片段:
canvas.clipPath(path) - 繪製形狀:
drawClippedRectangle() or drawText() - 還原先前的畫布狀態:
canvas.restore()
步驟 1:實作 drawDifferenceClippingExample(canvas)
新增程式碼來繪製第二個矩形,並使用兩個裁剪矩形之間的差異,建立相框效果。

使用下列程式碼,執行以下操作:
- 儲存畫布。
- 將畫布原點平移至開放空間,也就是第一列第二欄,第一個矩形的右側。
- 套用兩個剪裁矩形。
DIFFERENCE運算子會從第一個矩形減去第二個矩形。
- 呼叫
drawClippedRectangle()方法,繪製修改後的畫布。 - 還原畫布狀態。
private fun drawDifferenceClippingExample(canvas: Canvas) {
canvas.save()
// Move the origin to the right for the next rectangle.
canvas.translate(columnTwo,rowOne)
// Use the subtraction of two clipping rectangles to create a frame.
canvas.clipRect(
2 * rectInset,2 * rectInset,
clipRectRight - 2 * rectInset,
clipRectBottom - 2 * rectInset
)
// The method clipRect(float, float, float, float, Region.Op
// .DIFFERENCE) was deprecated in API level 26. The recommended
// alternative method is clipOutRect(float, float, float, float),
// which is currently available in API level 26 and higher.
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O){
canvas.clipRect(
4 * rectInset,4 * rectInset,
clipRectRight - 4 * rectInset,
clipRectBottom - 4 * rectInset,
Region.Op.DIFFERENCE
)
} else {
canvas.clipOutRect(
4 * rectInset,4 * rectInset,
clipRectRight - 4 * rectInset,
clipRectBottom - 4 * rectInset
)
}
drawClippedRectangle(canvas)
canvas.restore()
}- 執行應用程式,看起來應該會像這樣。

步驟 2:實作 drawCircularClippingExample(canvas)
接著,請加入程式碼來繪製矩形,並使用以圓形路徑建立的圓形剪裁區域,基本上就是移除 (不繪製) 圓形,因此會顯示灰色背景。

private fun drawCircularClippingExample(canvas: Canvas) {
canvas.save()
canvas.translate(columnOne, rowTwo)
// Clears any lines and curves from the path but unlike reset(),
// keeps the internal data structure for faster reuse.
path.rewind()
path.addCircle(
circleRadius,clipRectBottom - circleRadius,
circleRadius,Path.Direction.CCW
)
// The method clipPath(path, Region.Op.DIFFERENCE) was deprecated in
// API level 26. The recommended alternative method is
// clipOutPath(Path), which is currently available in
// API level 26 and higher.
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O) {
canvas.clipPath(path, Region.Op.DIFFERENCE)
} else {
canvas.clipOutPath(path)
}
drawClippedRectangle(canvas)
canvas.restore()
}步驟 3:實作 drawIntersectionClippingExample(canvas)
接著,在第二列和第二欄中新增程式碼,繪製兩個裁剪矩形的交集。


請注意,這個區域的外觀會因螢幕解析度而異。嘗試使用 smallRectOffset 維度變更可見區域的大小。smallRectOffset 值越小,螢幕上的區域就越大。
private fun drawIntersectionClippingExample(canvas: Canvas) {
canvas.save()
canvas.translate(columnTwo,rowTwo)
canvas.clipRect(
clipRectLeft,clipRectTop,
clipRectRight - smallRectOffset,
clipRectBottom - smallRectOffset
)
// The method clipRect(float, float, float, float, Region.Op
// .INTERSECT) was deprecated in API level 26. The recommended
// alternative method is clipRect(float, float, float, float), which
// is currently available in API level 26 and higher.
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O) {
canvas.clipRect(
clipRectLeft + smallRectOffset,
clipRectTop + smallRectOffset,
clipRectRight,clipRectBottom,
Region.Op.INTERSECT
)
} else {
canvas.clipRect(
clipRectLeft + smallRectOffset,
clipRectTop + smallRectOffset,
clipRectRight,clipRectBottom
)
}
drawClippedRectangle(canvas)
canvas.restore()
}
步驟 4:實作 drawCombinedClippingExample(canvas)
接著,合併圓形和矩形,並繪製任意路徑來定義剪裁區域。

private fun drawCombinedClippingExample(canvas: Canvas) {
canvas.save()
canvas.translate(columnOne, rowThree)
path.rewind()
path.addCircle(
clipRectLeft + rectInset + circleRadius,
clipRectTop + circleRadius + rectInset,
circleRadius,Path.Direction.CCW
)
path.addRect(
clipRectRight / 2 - circleRadius,
clipRectTop + circleRadius + rectInset,
clipRectRight / 2 + circleRadius,
clipRectBottom - rectInset,Path.Direction.CCW
)
canvas.clipPath(path)
drawClippedRectangle(canvas)
canvas.restore()
}
步驟 5:實作 drawRoundedRectangleClippingExample(canvas)
接著,新增圓角矩形,這是常用的剪裁形狀。

- 在頂層建立並初始化矩形變數。
RectF類別會以浮點數保留矩形座標。
private var rectF = RectF(
rectInset,
rectInset,
clipRectRight - rectInset,
clipRectBottom - rectInset
)- 實作
drawRoundedRectangleClippingExample()函式。addRoundRect()函式會採用矩形、圓角半徑的 x 和 y 值,以及圓角矩形輪廓的捲繞方向。Path.Direction會指定封閉形狀 (例如矩形、橢圓形) 新增至路徑時的方向。CCW代表逆時針。
private fun drawRoundedRectangleClippingExample(canvas: Canvas) {
canvas.save()
canvas.translate(columnTwo,rowThree)
path.rewind()
path.addRoundRect(
rectF,clipRectRight / 4,
clipRectRight / 4, Path.Direction.CCW
)
canvas.clipPath(path)
drawClippedRectangle(canvas)
canvas.restore()
}
步驟 6:實作 drawOutsideClippingExample(canvas)
將裁剪矩形的插邊加倍,即可裁剪矩形周圍的外部。

private fun drawOutsideClippingExample(canvas: Canvas) {
canvas.save()
canvas.translate(columnOne,rowFour)
canvas.clipRect(2 * rectInset,2 * rectInset,
clipRectRight - 2 * rectInset,
clipRectBottom - 2 * rectInset)
drawClippedRectangle(canvas)
canvas.restore()
}
步驟 7:實作 drawTranslatedTextExample(canvas)
繪製文字與其他形狀沒有太大差異,而且你可以對文字套用變形效果。舉例來說,你可以翻譯畫布並繪製文字,藉此翻譯文字。

- 實作下列函式。
private fun drawTranslatedTextExample(canvas: Canvas) {
canvas.save()
paint.color = Color.GREEN
// Align the RIGHT side of the text with the origin.
paint.textAlign = Paint.Align.LEFT
// Apply transformation to canvas.
canvas.translate(columnTwo,textRow)
// Draw text.
canvas.drawText(context.getString(R.string.translated),
clipRectLeft,clipRectTop,paint)
canvas.restore()
}- 執行應用程式,查看翻譯後的文字。

步驟 8:實作 drawSkewedTextExample(canvas)
你也可以傾斜文字。也就是以各種方式扭曲。

- 在
ClippedView中建立下列函式。
private fun drawSkewedTextExample(canvas: Canvas) {
canvas.save()
paint.color = Color.YELLOW
paint.textAlign = Paint.Align.RIGHT
// Position text.
canvas.translate(columnTwo, textRow)
// Apply skew transformation.
canvas.skew(0.2f, 0.3f)
canvas.drawText(context.getString(R.string.skewed),
clipRectLeft, clipRectTop, paint)
canvas.restore()
}- 執行應用程式,查看翻譯文字前繪製的傾斜文字。

quickReject() Canvas 方法可讓您檢查指定矩形或路徑是否完全位於目前可見區域外 (套用所有轉換後)。
建構較複雜的繪圖時,如果需要盡快完成,quickReject() 方法就非常實用。有了 quickReject(),您就能有效決定完全不必繪製的物件,而且不需要自行編寫交集邏輯。
- 如果矩形或路徑完全不會顯示在畫面上,
quickReject()方法會傳回true。如果部分重疊,您仍須自行檢查。 EdgeType可以是AA(反鋸齒:將邊緣四捨五入,因為邊緣可能會反鋸齒),也可以是BW(黑白:將邊緣四捨五入到最接近的像素邊界)。
quickReject() 有多個版本,您也可以在說明文件中找到。
| quickReject |
| quickReject |
| quickReject |
在本練習中,您要在文字下方和 clipRect 內繪製新列,做法與先前相同。
- 您首先會使用與
clipRect重疊的矩形inClipRectangle呼叫quickReject()。因此quickReject()會傳回 false,clipRect會填入BLACK,並繪製inClipRectangle矩形。

- 然後變更程式碼並呼叫
quickReject(),並使用notInClipRectangle。quickReject()現在會傳回 true,且clipRect會填入WHITE,而notInClipRectangle不會繪製。

如果繪圖很複雜,這項功能可快速判斷哪些形狀完全位於裁剪區域外,以及哪些形狀位於裁剪區域內 (部分或全部),因此您可能需要進行額外計算和繪圖。
步驟:實驗 quickReject()
- 在頂層建立額外列的 y 座標變數。
private val rejectRow = rowFour + rectInset + 2*clipRectBottom- 將下列
drawQuickRejectExample()函式新增至ClippedView。請詳閱程式碼,瞭解使用quickReject()的所有須知。
private fun drawQuickRejectExample(canvas: Canvas) {
val inClipRectangle = RectF(clipRectRight / 2,
clipRectBottom / 2,
clipRectRight * 2,
clipRectBottom * 2)
val notInClipRectangle = RectF(RectF(clipRectRight+1,
clipRectBottom+1,
clipRectRight * 2,
clipRectBottom * 2))
canvas.save()
canvas.translate(columnOne, rejectRow)
canvas.clipRect(
clipRectLeft,clipRectTop,
clipRectRight,clipRectBottom
)
if (canvas.quickReject(
inClipRectangle, Canvas.EdgeType.AA)) {
canvas.drawColor(Color.WHITE)
}
else {
canvas.drawColor(Color.BLACK)
canvas.drawRect(inClipRectangle, paint
)
}
canvas.restore()
}- 在
onDraw()中,取消註解drawQuickRejectExample()的呼叫。 - 執行應用程式,您會看到黑色矩形 (填滿的剪輯區域) 和
inClipRectangle的部分,因為兩個矩形重疊,所以quickReject()會傳回false並繪製inClipRectangle。

- 在
drawQuickRejectExample()中,變更程式碼以針對notInClipRectangle.執行quickReject()。現在quickReject()會傳回true,且裁剪區域會填滿白色。

下載完成的程式碼研究室程式碼。
$ git clone https://github.com/googlecodelabs/android-kotlin-drawing-clipping
您也可以將存放區下載為 ZIP 檔案、將其解壓縮,並在 Android Studio 中開啟。
- 活動的
Context會維護狀態,保留Canvas的轉換和裁剪區域。 - 使用
canvas.save()和canvas.restore()繪圖,並返回畫布的原始狀態。 - 如要在畫布上繪製多個形狀,您可以計算形狀的位置,也可以移動 (平移) 繪圖表面的原點。後者可讓您更輕鬆地為重複的繪圖序列建立公用程式方法。
- 剪裁區域可以是任何形狀、形狀組合或路徑。
- 您可以新增、減去和交集裁剪區域,取得所需的確切區域。
- 您可以轉換畫布,對文字套用轉換效果。
quickReject()Canvas方法可讓您檢查指定矩形或路徑是否完全位於目前可見區域之外。
Udacity 課程:
Android 開發人員說明文件:
Canvas類別Bitmap類別View類別Paint類別Bitmap.config設定Region.Op運算子Path類別Canvas類別Bitmap類別View類別Paint類別Bitmap.config設定Region.Op運算子Path類別android.graphics圖像工具Bitmap.ConfigCanvas設定- 畫布和可繪項目
- canvas.translate() 的用途為何?
- 瞭解 Canvas 內容的 save() 和 restore()
- 剪輯
- 過度繪製。
@JvmOverloads
如要深入瞭解 Android 架構如何繪製到畫面上,請參閱「圖像架構」系列文章。
本節列出的作業可由課程講師指派給學習本程式碼研究室的學員。講師可自由採取以下行動:
- 視需要指派作業。
- 告知學員如何繳交作業。
- 為作業評分。
講師可以視需求使用全部或部分建議內容,也可以自由指派任何其他合適的作業。
如果您是自行學習本程式碼研究室,不妨利用這些作業驗收學習成果。
回答問題
第 1 題
您會呼叫哪個方法,有效排除要繪製的形狀?
▢ excludeFromDrawing()
▢ quickReject()
▢ onDraw()
▢ clipRect()
第 2 題
Canvas.save() 和 Canvas.restore() 會儲存及還原哪些資訊?
▢ 顏色、線條寬度等。
▢ 僅限目前的轉換
▢ 目前的轉換和裁剪區域
▢ 僅限目前的剪輯區域
第 3 題
Paint.Align 指定:
▢ 如何對齊下列繪圖形狀
▢ 文字的繪製起點位於來源的哪一側
▢ 裁剪區域中的對齊位置
▢ 要將文字的哪一側對齊原點
如要查看本課程其他程式碼研究室的連結,請參閱 Android Kotlin 進階功能程式碼研究室登陸頁面。