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 die Größe und das Layout des entsprechenden Composeables zu beeinflussen, in dem es verwendet wird . Ein BitmapPainter nimmt einen ImageBitmap auf, der ein 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.

Ein Painter unterscheidet sich von einem DrawModifier, das sich streng innerhalb der ihm zugewiesenen Grenzen zeichnet und keinen Einfluss auf die Größe oder das Layout des Composeables hat.

Um einen benutzerdefinierten Maler zu erstellen, erweitern Sie die Klasse Painter und implementieren Sie die Methode onDraw, die den Zugriff auf die DrawScope zum Zeichnen benutzerdefinierter Grafiken ermöglicht. 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
    }
}

Jetzt, da wir unsere benutzerdefinierte Painter haben, können wir wie unten beschrieben ein beliebiges Bild auf 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()
)

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

Benutzerdefinierte Malfunktion, mit der zwei Bilder übereinander gelegt werden
Abbildung 1: Benutzerdefinierter Painter, der zwei Bilder übereinander legt

Ein benutzerdefinierter Maler kann auch mit Modifier.paint(customPainter) verwendet werden, um Inhalte auf eine Komposition 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 **/ }