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

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

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

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

หากต้องการสร้างโปรแกรมวาดภาพที่กำหนดเอง ให้ขยายคลาส 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 รูปด้วยโปรแกรมวาดภาพที่กำหนดเองมีดังนี้

เครื่องมือวาดรูปที่กำหนดเองซึ่งวางซ้อนรูปภาพ 2 รูปไว้ด้วยกัน
รูปที่ 1: เครื่องมือวาดภาพที่กำหนดเองซึ่งวางซ้อนรูปภาพ 2 รูปทับกัน

นอกจากนี้ คุณยังใช้โปรแกรมวาดภาพที่กำหนดเองร่วมกับ 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 **/ }