طلاء مخصّص

في أداة 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()
)

يمكنك الاطّلاع أدناه على نتيجة دمج الصورتَين باستخدام أداة رسم مخصّصة:

أداة الرسم المخصّصة التي تتراكب صورتان فوق بعضهما
الشكل 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 **/ }