نقاش سفارشی

در Compose، از شیء Painter برای نشان دادن چیزی که می‌تواند رسم شود (جایگزینی برای میاناهای برنامه‌سازی کاربردی Drawable تعریف‌شده در Android) و بر اندازه‌گیری و چیدمان تابع ترکیب‌پذیر مربوطه که از آن استفاده می‌کند تأثیر بگذارد استفاده می‌شود . ‫A 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()
)

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

«نقاش سفارشی» که دو تصویر را روی هم قرار می‌دهد
شکل ۱: «نقاش سفارشی» که دو تصویر را روی هم می‌اندازد

از نقاش سفارشی نیز می‌توان با 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 **/ }