ช่างทาสีตามสั่ง

ใน Compose ออบเจ็กต์ Painter ใช้เพื่อแสดงสิ่งที่วาดได้ (แทนที่ API Drawable ที่กำหนดไว้ใน Android) และส่งผลต่อการวัดผลและเลย์เอาต์ของ Composable ที่เกี่ยวข้องซึ่งใช้ API นั้น BitmapPainter ใช้ ImageBitmap ที่สามารถวาด Bitmap บนหน้าจอได้

สำหรับกรณีการใช้งานส่วนใหญ่ การใช้ฟังก์ชัน painterResource() จะแสดงผล Painter ที่ถูกต้องสำหรับชิ้นงาน (เช่น BitmapPainter หรือ VectorPainter) อ่านข้อมูลเพิ่มเติมเกี่ยวกับความแตกต่างระหว่างทั้ง 2 อย่างได้ที่ส่วนImageBitmap กับ ImageVector

Painter แตกต่างจาก DrawModifier ซึ่งจะวาดภายในขอบเขตที่กำหนดไว้อย่างเคร่งครัด และไม่มีผลต่อการวัดหรือเลย์เอาต์ของ Composable

หากต้องการสร้าง Painter ที่กำหนดเอง ให้ขยายคลาส Painter และใช้เมธอด onDraw ซึ่งจะช่วยให้เข้าถึง DrawScope เพื่อวาดกราฟิกที่กำหนดเองได้ นอกจากนี้ คุณยังลบล้าง intrinsicSize ซึ่งจะใช้เพื่อส่งผลต่อ Composable ที่มีอยู่ได้ด้วย

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

รูปภาพต่อไปนี้แสดงผลลัพธ์ของการรวมรูปภาพ 2 รูปเข้าด้วยกันโดยใช้ CustomPainter

Custom Painter ที่วางรูปภาพ 2 รูปซ้อนกัน
รูปที่ 1: Custom Painter ที่วางรูปภาพ 2 รูปซ้อนกัน

นอกจากนี้ คุณยังใช้ CustomPainter กับ Modifier.paint(customPainter) เพื่อวาดเนื้อหาไปยัง Composable ได้ดังนี้

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