Maler und Lackierer

In Compose wird ein Painter-Objekt verwendet, um etwas darzustellen, das gezeichnet werden kann (als Ersatz für die in Android definierten Drawable-APIs) und die Messung und das Layout der entsprechenden zusammensetzbaren Funktion zu beeinflussen, in der es verwendet wird . Ein BitmapPainter nimmt ein ImageBitmap, das eine Bitmap auf dem Bildschirm zeichnen kann.

In den meisten Anwendungsfällen wird mit der painterResource() oben der richtige Painter für das Asset zurückgegeben (d. h. BitmapPainter oder VectorPainter). Weitere Informationen zu den Unterschieden zwischen den beiden finden Sie im Abschnitt ImageBitmap vs. ImageVector.

Eine Painter unterscheidet sich von einer DrawModifier, die sich strikt innerhalb der ihnen zugewiesenen Grenzen zieht und keinen Einfluss auf die Messung oder das Layout der zusammensetzbaren Funktion hat.

Erweitern Sie zum Erstellen eines benutzerdefinierten Painters die Painter-Klasse und implementieren Sie die Methode onDraw, die Zugriff auf den DrawScope ermöglicht, um benutzerdefinierte Grafiken zu zeichnen. Sie können auch das intrinsicSize überschreiben, mit dem die Composable beeinflusst wird, in der sie enthalten ist:

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

Da wir nun das benutzerdefinierte Painter haben, können wir ein beliebiges Bild so über das Quell-Image legen:

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

Unten sehen Sie, wie sich die beiden Bilder mit einem benutzerdefinierten Painter kombinieren lassen:

Benutzerdefinierter Painter, bei dem zwei Bilder übereinander gelegt werden
Abbildung 1: Benutzerdefinierter Painter, bei dem zwei Bilder übereinandergelegt werden

Ein benutzerdefinierter Painter kann auch mit dem Modifier.paint(customPainter) verwendet werden, um den Inhalt wie folgt in eine zusammensetzbare Funktion zu zeichnen:

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