Pittura personalizzata

In Compose, un oggetto Painter viene utilizzato per rappresentare qualcosa che può essere disegnato (una sostituzione delle API Drawable definite in Android) e influenzare la misurazione e il layout del componibile corrispondente che lo utilizza . Un BitmapPainter prende un ImageBitmap che può disegnare Bitmap sullo schermo.

Nella maggior parte dei casi d'uso, l'utilizzo del criterio painterResource() riportato sopra restituisce il pittore corretto per la risorsa (ad es. BitmapPainter o VectorPainter). Per ulteriori informazioni sulle differenze tra i due, leggi la sezione ImageBitmap vs ImageVector.

Un elemento Painter è diverso da un DrawModifier, che rispetta rigorosamente i limiti assegnati e non influisce sulla misurazione o sul layout dell'elemento componibile.

Per creare un disegno personalizzato, estendi la classe Painter e implementa il metodo onDraw, che permette di accedere a DrawScope per disegnare immagini personalizzate. Puoi anche eseguire l'override di intrinsicSize, che verrà utilizzato per influenzare il componibile in esso contenuto:

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
    }
}

Ora che abbiamo il Painter personalizzato, possiamo sovrapporre qualsiasi immagine all'immagine di origine nel seguente modo:

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()
)

Di seguito è possibile vedere l'output della combinazione delle due immagini con un pittore personalizzato:

Pittore personalizzato che sovrappone due immagini una sull&#39;altra
Figura 1: pittore personalizzato che sovrappone due immagini una sull'altra

È possibile utilizzare un pittore personalizzato anche con la Modifier.paint(customPainter) per disegnare i contenuti in un componibile in questo modo:

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 **/ }