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 beeinflussen, die es verwendet . Für BitmapPainter wird ein ImageBitmap-Objekt verwendet, das eine Bitmap auf dem Bildschirm zeichnen kann.

In den meisten Anwendungsfällen wird bei Verwendung des obigen painterResource() der korrekte Maler für das Asset zurückgegeben (z.B. BitmapPainter oder VectorPainter). Weitere Informationen zu den Unterschieden zwischen den beiden finden Sie im Abschnitt ImageBitmap im Vergleich zu ImageVector.

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

Wenn Sie einen benutzerdefinierten Painter erstellen möchten, erweitern Sie die Klasse Painter und implementieren Sie die Methode onDraw. Diese ermöglicht den Zugriff auf DrawScope zum Zeichnen benutzerdefinierter Grafiken. Sie können auch die intrinsicSize überschreiben, die verwendet wird, um die zusammensetzbare Funktion 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
    }
}

Mit der benutzerdefinierten Painter können wir nun ein beliebiges Bild über das 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()
)

Das Ergebnis der Kombination der beiden Bilder mit einem benutzerdefinierten Painter sieht so aus:

Custom Painter, der zwei Bilder überlagert
Abbildung 1: Custom Painter, der zwei Bilder überlagert

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