Compose 中的圖形

Jetpack Compose 可讓您以更簡單的方式處理自訂圖形。多數應用程式都必須能夠精準地控制畫面上所繪製的內容。這可能是簡單地將方塊或圓形置於畫面上的正確位置,也可能是精心安排多種不同樣式的圖形元素。透過 Compose 的宣告式方法,所有圖形設定都集中在一處進行,不必劃分為方法呼叫和 Paint 輔助物件。Compose 會有效率地建立及更新所需物件。

Compose 內建的宣告式圖形

Compose 將宣告式方法延伸至圖形處理方式。Compose 的方法提供許多優點:

  • Compose 可在圖形元素中盡量減少使用狀態,協助您避免掉入狀態的程式設計誤區
  • 在您進行繪圖時,所有選項都會出現在預期的確切位置,也就是在可組合函式中的正確位置。
  • Compose 的圖形 API 能夠有效率地建立及釋放物件。

Canvas

自訂圖形的核心可組合項是 Canvas。將 Canvas 置入版面配置的方式,與置入其他 Compose UI 元素的方式相同。在 Canvas 中,您可在精準控制樣式和位置的情況下繪製元素。

舉例來說,以下程式碼會建立 Canvas 可組合項,以填滿其父項元素中的所有可用空間:

Canvas(modifier = Modifier.fillMaxSize()) {
}

Canvas 會自動顯示 DrawScope,這是能自行維護狀態的限定範圍繪圖環境,也能讓您為一組圖形元素設定參數。DrawScope 提供多個實用的欄位,例如 sizeSize 物件可指定 DrawScope 目前的尺寸和尺寸上限。

例如,假設您想繪製從畫布右上角到左下角的對角線,可以新增一個 drawLine 可組合項來執行:

Canvas(modifier = Modifier.fillMaxSize()) {
    val canvasWidth = size.width
    val canvasHeight = size.height

    drawLine(
        start = Offset(x = canvasWidth, y = 0f),
        end = Offset(x = 0f, y = canvasHeight),
        color = Color.Blue
    )
}

手機畫面上繪製了一條細對角線。

圖 1:使用 drawLine 繪製一條跨畫布的線條。程式碼設定了線條顏色,但使用了預設寬度。

您可以使用其他參數自訂繪圖。例如,根據預設,系統會以髮絲寬度繪製線條,無論繪圖的比例為何,皆顯示為一個像素的寬度。只要設定 strokeWidth 值即可覆寫這個預設值:

Canvas(modifier = Modifier.fillMaxSize()) {
    val canvasWidth = size.width
    val canvasHeight = size.height

    drawLine(
        start = Offset(x = canvasWidth, y = 0f),
        end = Offset(x = 0f, y = canvasHeight),
        color = Color.Blue,
        strokeWidth = 5F
    )
}

手機畫面上繪製了一條較粗的對角線。

圖 2:透過覆寫預設寬度來修改圖 1 中的線條。

系統還提供眾多其他簡單的繪圖函式,例如 drawRectdrawCircle。舉例來說,以下程式碼會在畫布中央繪製一個實心圓,其直徑等於畫布短邊尺寸的一半:

Canvas(modifier = Modifier.fillMaxSize()) {
    val canvasWidth = size.width
    val canvasHeight = size.height
    drawCircle(
        color = Color.Blue,
        center = Offset(x = canvasWidth / 2, y = canvasHeight / 2),
        radius = size.minDimension / 4
    )
}

手機畫面的中央有一個藍色的圓形。

圖 3:使用 drawCircle 在畫布中央放置一個圓形。根據預設,drawCircle 會繪製實心圓,因此不需明確指定該項設定。

繪圖函式提供實用的預設參數。例如,根據預設,drawRectangle() 會填滿整個父項範圍,而 drawCircle() 的半徑會等於其父項短邊尺寸的一半。就像 Kotlin 的一貫使用方式,如要讓程式碼更加簡潔清楚,建議您善加利用預設的參數值,只要設定需要變更的參數即可。如要善用這項做法,由於您所繪製的元素會根據父項範圍的設定來決定本身的預設設定,因此只要在 DrawScope 繪圖方法中提供明確的參數即可。

DrawScope

如前所述,每個 Compose Canvas 都會顯示一個 DrawScope,這是限定範圍的繪圖環境,您可在其中實際發出繪圖指令。

舉例來說,以下程式碼會在畫布左上角繪製一個矩形:

Canvas(modifier = Modifier.fillMaxSize()) {
    val canvasQuadrantSize = size / 2F
    drawRect(
        color = Color.Green,
        size = canvasQuadrantSize
    )
}

您可以使用 DrawScope.inset() 函式調整目前範圍的預設參數、變更繪圖界線並據此平移繪圖。inset() 等作業會套用至對應 lambda 內的所有繪圖作業:

val canvasQuadrantSize = size / 2F
inset(50F, 30F) {
    drawRect(
        color = Color.Green,
        size = canvasQuadrantSize
    )
}

DrawScope 還提供其他簡單的轉換作業,例如 rotate()。舉例來說,以下程式碼可繪製一個矩形,填滿畫布中央九分之一的空間:

val canvasSize = size
val canvasWidth = size.width
val canvasHeight = size.height
drawRect(
    color = Color.Gray,
    topLeft = Offset(x = canvasWidth / 3F, y = canvasHeight / 3F),
    size = canvasSize / 3F
)

手機畫面中央顯示一個實心矩形。

圖 4:使用 drawRect 在畫面中央繪製一個實心矩形。

您可以為矩形的 DrawScope 函式套用旋轉作業,藉此旋轉矩形:

rotate(degrees = 45F) {
    drawRect(
        color = Color.Gray,
        topLeft = Offset(x = canvasWidth / 3F, y = canvasHeight / 3F),
        size = canvasSize / 3F
    )
}

手機畫面中央的矩形旋轉了 45 度。

圖 5:我們利用 rotate() 為目前的繪圖範圍套用旋轉作業,將矩形旋轉 45 度。

如果您想為繪圖套用多項轉換作業,最佳方式並不是建立巢狀的 DrawScope 環境,而是使用 withTransform() 函式建立及套用單一轉換作業,並在其中結合所有需要的變更。與針對個別轉換作業發出巢狀呼叫相較,使用 withTransform() 會更有效率,這是因為所有轉換作業都會透過單一作業一併執行,Compose 就不需逐一計算及儲存個別巢狀轉換作業。

例如,以下程式碼會為矩形透用平移和旋轉作業:

withTransform({
    translate(left = canvasWidth / 5F)
    rotate(degrees = 45F)
}) {
    drawRect(
        color = Color.Gray,
        topLeft = Offset(x = canvasWidth / 3F, y = canvasHeight / 3F),
        size = canvasSize / 3F
    )
}

手機上的矩形已旋轉並搬移到畫面的側邊。

圖 6:我們在此使用 withTransform 同時套用旋轉和平移作業,將矩形旋轉並向左搬移。