Painter tuỳ chỉnh

Trong Compose, đối tượng Painter được dùng để thể hiện nội dung nào đó có thể vẽ (thay thế cho các API Drawable được xác định trong Android) và tạo tác động đến hoạt động đo lường cùng với bố cục của thành phần kết hợp tương ứng đang sử dụng đối tượng đó. BitmapPainter lấy ImageBitmap có khả năng vẽ Bitmap trên màn hình.

Đối với hầu hết các trường hợp sử dụng, việc sử dụng painterResource() ở trên sẽ trả về đúng Painter cho thành phần (tức là BitmapPainter hoặc VectorPainter). Để biết thêm thông tin về sự khác biệt giữa 2 loại này, hãy đọc phần ImageBitmap so với ImageVector.

Painter khác với DrawModifier ở chỗ khi được dùng sẽ vẽ một cách nghiêm ngặt trong giới hạn được cấp và hoàn toàn không tác động đến hoạt động đo lường hoặc bố cục của thành phần kết hợp.

Để tạo một Painter tuỳ chỉnh, hãy mở rộng lớp Painter và triển khai phương thức onDraw. Phương thức này cho phép truy cập vào DrawScope để vẽ các đồ hoạ tuỳ chỉnh. Bạn cũng có thể ghi đè intrinsicSize dùng để tạo tác động đến Thành phần kết hợp có ở trong:

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

Bây giờ, chúng ta đã có Painter tuỳ chỉnh nên có thể phủ bất kỳ hình ảnh nào lên hình ảnh nguồn như sau:

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()
)

Bạn có thể xem kết quả kết hợp 2 hình ảnh này bằng một Painter tuỳ chỉnh ở bên dưới:

Painter tuỳ chỉnh phủ 2 hình ảnh lên nhau
Hình 1: Painter tuỳ chỉnh phủ 2 hình ảnh lên nhau

Bạn cũng có thể sử dụng Painter tuỳ chỉnh với Modifier.paint(customPainter) để vẽ nội dung lên trên thành phần kết hợp như sau:

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 **/ }