Maler und Lackierer

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

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

Ein Painter unterscheidet sich von einem DrawModifier, das nur innerhalb der ihm zugewiesenen Grenzen gezeichnet wird und keinen Einfluss auf die Messung oder das Layout des Composables hat.

Wenn Sie einen benutzerdefinierten Painter erstellen möchten, erweitern Sie die Klasse Painter und implementieren Sie die Methode onDraw, die den Zugriff auf DrawScope ermöglicht, um benutzerdefinierte Grafiken zu zeichnen. Sie können auch die intrinsicSize überschreiben, die verwendet wird, um die Composable zu beeinflussen, 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
    }
}

Nachdem wir nun unser benutzerdefiniertes Painter haben, können wir jedes beliebige Bild wie folgt über unser Quellbild 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()
)

Die Ausgabe der Kombination der beiden Bilder mit einem benutzerdefinierten Painter ist unten zu sehen:

Benutzerdefinierte Painter-Klasse, die zwei Bilder übereinander legt
Abbildung 1: Benutzerdefinierte Painter-Klasse, die zwei Bilder übereinander legt

Ein benutzerdefinierter Painter kann auch mit dem Modifier.paint(customPainter) verwendet werden, um den Inhalt in ein Composable 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 **/ }