自訂繪圖工具

在 Compose 中,Painter 物件用於代表可繪製的內容 (取代 Android 中定義的 Drawable API),並且會影響使用該物件的對應可組合項的尺寸和版面配置。BitmapPainter 會採用可在畫面上繪製 BitmapImageBitmap

在大多數用途上,使用上述的 painterResource() 會傳回素材資源的正確繪圖工具 (例如 BitmapPainterVectorPainter)。如要進一步瞭解這兩者之間的差異,請參閱 ImageBitmap 與 ImageVector 一節。

PainterDrawModifier 不同,後者只會在指定的邊界範圍內繪製,不會影響可組合項的尺寸或版面配置。

如要建立自訂繪圖工具,只要擴充 Painter 類別並實作 onDraw 方法,即可存取 DrawScope 以繪製自訂圖形。您也可以覆寫 intrinsicSize,這用來影響其包含的可組合項:

class OverlayImagePainter constructor(
    private val image: ImageBitmap,
    private val imageOverlay: ImageBitmap,
    private val srcOffset: IntOffset = IntOffset.Zero,
    private val srcSize: IntSize = IntSize(image.width, image.height),
    private val overlaySize: IntSize = IntSize(imageOverlay.width, imageOverlay.height)
) : Painter() {

    private val size: IntSize = validateSize(srcOffset, srcSize)
    override fun DrawScope.onDraw() {
        // draw the first image without any blend mode
        drawImage(
            image,
            srcOffset,
            srcSize,
            dstSize = IntSize(
                this@onDraw.size.width.roundToInt(),
                this@onDraw.size.height.roundToInt()
            )
        )
        // draw the second image with an Overlay blend mode to blend the two together
        drawImage(
            imageOverlay,
            srcOffset,
            overlaySize,
            dstSize = IntSize(
                this@onDraw.size.width.roundToInt(),
                this@onDraw.size.height.roundToInt()
            ),
            blendMode = BlendMode.Overlay
        )
    }

    /**
     * Return the dimension of the underlying [ImageBitmap] as it's intrinsic width and height
     */
    override val intrinsicSize: Size get() = size.toSize()

    private fun validateSize(srcOffset: IntOffset, srcSize: IntSize): IntSize {
        require(
            srcOffset.x >= 0 &&
                srcOffset.y >= 0 &&
                srcSize.width >= 0 &&
                srcSize.height >= 0 &&
                srcSize.width <= image.width &&
                srcSize.height <= image.height
        )
        return srcSize
    }
}

現在我們已建立自訂 Painter,可以將任何圖片重疊在來源圖片上方,如下所示:

val rainbowImage = ImageBitmap.imageResource(id = R.drawable.rainbow)
val dogImage = ImageBitmap.imageResource(id = R.drawable.dog)
val customPainter = remember {
    OverlayImagePainter(dogImage, rainbowImage)
}
Image(
    painter = customPainter,
    contentDescription = stringResource(id = R.string.dog_content_description),
    contentScale = ContentScale.Crop,
    modifier = Modifier.wrapContentSize()
)

結合兩張圖片與自訂繪圖工具的輸出內容如下所示:

自訂可將兩張圖片互相重疊的 Painter
圖 1:自訂可將兩張圖片互相重疊的 Painter

自訂繪圖工具也可以與 Modifier.paint(customPainter) 搭配使用,將內容繪製到可組合項中,如下所示:

val rainbowImage = ImageBitmap.imageResource(id = R.drawable.rainbow)
val dogImage = ImageBitmap.imageResource(id = R.drawable.dog)
val customPainter = remember {
    OverlayImagePainter(dogImage, rainbowImage)
}
Box(
    modifier =
    Modifier.background(color = Color.Gray)
        .padding(30.dp)
        .background(color = Color.Yellow)
        .paint(customPainter)
) { /** intentionally empty **/ }