طلاء مخصّص

في 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: Custom 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 **/ }