Android Compose UI Canvas 自定義繪圖
當 Compose 提供的高階元件(如 Box, Image, Button)無法滿足複雜的視覺需求時,我們可以使用底層的 Canvas API 直接在畫布上繪製任意形狀、圖表或路徑。
核心概念:DrawScope 與 座標系
在 Compose 中使用 Canvas 時,你是在 DrawScope 中撰寫程式碼。這是一個受限的環境,專為高效繪圖設計。
- 座標系統:左上角為
(0, 0)。X 軸向右增加,Y 軸向下增加。 - 單位:底層繪圖 API 使用的是 像素 (Pixels)。雖然
DrawScope提供了size(像素) 和轉換工具,但如果你需要 DP,必須自行轉換。
Canvas(modifier = Modifier.fillMaxSize()) {
// size 屬性代表目前畫布的維度 (以像素為單位)
val canvasWidth = size.width
val canvasHeight = size.height
// 繪製一個基礎圓心
drawCircle(
color = Color.Blue,
center = Offset(x = canvasWidth / 2, y = canvasHeight / 2),
radius = size.minDimension / 4
)
}
常用繪圖函式
除了 drawCircle 和 drawLine,DrawScope 還提供了豐富的基礎形狀:
drawRect:繪製矩形。drawRoundRect:繪製帶圓角的矩形。drawOval:繪製橢圓。drawArc:繪製圓弧(可用於環狀圖或進度條)。
Canvas(modifier = Modifier.size(200.dp)) {
// 繪製一個空心圓弧
drawArc(
color = Color.Red,
startAngle = 0f, // 從 3 點鐘方向開始
sweepAngle = 270f, // 旋轉 270 度
useCenter = false, // 是否連接圓心
style = Stroke(width = 8.dp.toPx()) // 設定為空心線條
)
}
變形處理 (Transformations)
與其手動計算每個點的偏移,不如使用變形 API。這通常更直觀且性能更佳:
Canvas(modifier = Modifier.size(100.dp)) {
// 所有的變形都發生在 inset 區塊內
inset(20f, 20f) {
rotate(degrees = 45f) {
drawRect(color = Color.Blue)
}
}
}
常用的變換包括:translate (平移)、rotate (旋轉)、scale (縮放) 以及 inset (內縮區間)。
實戰範例:簡易圓餅圖 (Pie Chart)
以下程式碼展示如何根據數據比例,動態繪製一個圓餅圖。
@Composable
fun SimplePieChart(proportions: List<Float>, colors: List<Color>) {
Canvas(modifier = Modifier.size(200.dp)) {
var startAngle = -90f // 從上方開始繪製
proportions.forEachIndexed { index, prop ->
val sweepAngle = prop * 360f
drawArc(
color = colors[index],
startAngle = startAngle,
sweepAngle = sweepAngle,
useCenter = true
)
startAngle += sweepAngle
}
}
}
存取原生 Android Canvas
如果你需要的功能在 Compose DrawScope 中找不到(例如舊有的 drawText 或第三方圖形庫),你可以暫時切換回原生 Android Canvas:
Canvas(modifier = Modifier.fillMaxSize()) {
drawIntoCanvas { canvas ->
val nativeCanvas = canvas.nativeCanvas
// 這裡可以使用 nativeCanvas.drawText(...) 等原生方法
}
}
效能建議與最佳實踐
- 避免在畫布內配置物件:
Canvas的程式碼會頻繁重新執行。絕對不要在Canvas { ... }區塊內呼叫Path()或Paint()。請在remember區塊內建立它們。 - 利用
drawBehind:如果你只是想在某個 Composable 下方畫背景,使用Modifier.drawBehind通常比開一個Canvas更省資源。 - 區分重組 (Recomposition) 與重繪 (Invalidation):改變
remember中的普通變數通常不會觸發重繪。若要讓畫布動起來,請使用 Compose 的 State。
Canvas 是 Compose 中最強大的「畫筆」。雖然它的門檻較高,但對於需要精細像素控制的應用場景(如:圖表、相機濾鏡、遊戲 UI)來說是不可或缺的。