نقاش سفارشی

در Compose، از شیء Painter برای نمایش چیزی که قابل‌رسم است (جایگزینی برای میاناهای برنامه‌سازی کاربردی Drawable تعریف‌شده در Android) و تأثیرگذاری بر اندازه‌گیری و چیدمان ترکیب‌شدنی مربوطه که از آن استفاده می‌کند استفاده می‌شود . ‫A BitmapPainter takes an ImageBitmap that can draw a Bitmap on screen.

برای اکثر موارد استفاده، استفاده از تابع painterResource()، نقاش صحیح را برای دارایی (مانند BitmapPainter یا VectorPainter) برمی‌گرداند. برای اطلاعات بیشتر درباره تفاوت‌های این دو، بخش ImageBitmap درمقابل ImageVector را بخوانید.

Painter با DrawModifier متفاوت است، که به‌طور دقیق در محدوده‌هایی که به آن داده شده است ترسیم می‌شود و هیچ تأثیری بر اندازه‌گیری یا چیدمان composable ندارد.

برای ایجاد یک نقاش سفارشی، کلاس 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()
)

شکل زیر خروجی ترکیب دو تصویر با یک نقاش سفارشی را نشان می‌دهد:

Custom Painter که دو تصویر را روی یکدیگر قرار می‌دهد
شکل ۱: 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 **/ }