طلاء مخصّص

في Compose، يتم استخدام كائن Painter لتمثيل عنصر يمكن رسمه (وهو بديل لواجهات برمجة التطبيقات Drawable المحدّدة في Android) والتأثير في قياس وتنسيق الدالة البرمجية المركّبة المقابلة التي تستخدمه . تأخذ BitmapPainter ImageBitmap يمكنها رسم Bitmap على الشاشة.

في معظم حالات الاستخدام، تعرض الدالة painterResource() أداة الرسم الصحيحة للعنصر (مثل BitmapPainter أو VectorPainter). لمزيد من المعلومات حول الاختلافات بينهما، يمكنك الاطّلاع على القسم ImageBitmap مقابل ImageVector.

يختلف Painter عن DrawModifier، الذي يرسم بدقة ضمن الحدود المحدّدة له ولا يؤثّر في قياس أو تخطيط العنصر القابل للإنشاء.

لإنشاء أداة رسم مخصّصة، عليك توسيع فئة 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()
)

يوضّح الشكل التالي ناتج دمج الصورتَين باستخدام أداة رسم مخصّصة:

Custom Painter الذي يضع صورتين فوق بعضهما البعض
الشكل 1: أداة رسم مخصّصة تراكب صورتَين فوق بعضهما

يمكن أيضًا استخدام أداة رسم مخصّصة مع 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 **/ }