Grafikmodifikatoren

Zusätzlich zu den Canvas-Kompositionen bietet Compose mehrere nützliche GrafikenModifiers, mit denen sich benutzerdefinierte Inhalte erstellen lassen. Diese Modifikatoren sind nützlich, da sie auf jedes Kompositionenelement angewendet werden können.

Zeichenmodifikatoren

Alle Zeichenbefehle werden in Compose mit einem Zeichenmodifikator ausgeführt. In Compose gibt es drei Hauptmodifikatoren für das Zeichnen:

Der Basis-Modifikator für das Zeichnen ist drawWithContent. Hier können Sie die Zeichnungsreihenfolge Ihres Composables und die Zeichnungsbefehle festlegen, die im Modifikator ausgegeben werden. drawBehind ist ein praktischer Wrapper um drawWithContent, wobei die Zeichenreihenfolge hinter dem Inhalt der zusammensetzbaren Funktion festgelegt ist. drawWithCache ruft entweder onDrawBehind oder onDrawWithContent auf und bietet einen Mechanismus zum Caching der darin erstellten Objekte.

Modifier.drawWithContent: Zeichnungsreihenfolge auswählen

Mit Modifier.drawWithContent können Sie DrawScope-Vorgänge vor oder nach dem Inhalt des Composeables ausführen. Rufen Sie drawContent auf, um den Inhalt des Composeables zu rendern. Mit diesem Modifikator können Sie die Reihenfolge der Vorgänge festlegen, wenn Ihr Inhalt vor oder nach den benutzerdefinierten Zeichenvorgängen gezeichnet werden soll.

Wenn Sie beispielsweise einen radialen Farbverlauf über Ihren Inhalt rendern möchten, um einen Taschenlampen-Schlüssellocheffekt auf der Benutzeroberfläche zu erzeugen, gehen Sie so vor:

var pointerOffset by remember {
    mutableStateOf(Offset(0f, 0f))
}
Column(
    modifier = Modifier
        .fillMaxSize()
        .pointerInput("dragging") {
            detectDragGestures { change, dragAmount ->
                pointerOffset += dragAmount
            }
        }
        .onSizeChanged {
            pointerOffset = Offset(it.width / 2f, it.height / 2f)
        }
        .drawWithContent {
            drawContent()
            // draws a fully black area with a small keyhole at pointerOffset that’ll show part of the UI.
            drawRect(
                Brush.radialGradient(
                    listOf(Color.Transparent, Color.Black),
                    center = pointerOffset,
                    radius = 100.dp.toPx(),
                )
            )
        }
) {
    // Your composables here
}

Abbildung 1: Modifier.drawWithContent wird über einem Composable verwendet, um eine Taschenlampen-UI zu erstellen.

Modifier.drawBehind: Hinter einem Composeable zeichnen

Mit Modifier.drawBehind können Sie DrawScope-Vorgänge hinter den zusammensetzbaren Inhalten ausführen, die auf dem Bildschirm angezeigt werden. Wenn Sie sich die Implementierung von Canvas ansehen, stellen Sie möglicherweise fest, dass es sich dabei nur um eine praktische Ummantelung von Modifier.drawBehind handelt.

So zeichnen Sie hinter Text ein abgerundetes Rechteck:

Text(
    "Hello Compose!",
    modifier = Modifier
        .drawBehind {
            drawRoundRect(
                Color(0xFFBBAAEE),
                cornerRadius = CornerRadius(10.dp.toPx())
            )
        }
        .padding(4.dp)
)

Das führt zu folgendem Ergebnis:

Text und Hintergrund, die mit Modifier.drawBehind gezeichnet wurden
Abbildung 2: Text und Hintergrund, die mit Modifier.drawBehind gezeichnet wurden

Modifier.drawWithCache: Zeichnen und Caching von Zeichnungsobjekten

Modifier.drawWithCache speichert die darin erstellten Objekte im Cache. Die Objekte werden im Cache gespeichert, solange die Größe des Zeichenbereichs gleich bleibt oder sich die gelesenen Statusobjekte nicht geändert haben. Dieser Modifikator ist nützlich, um die Leistung von Zeichenaufrufen zu verbessern, da Objekte, die bei „draw“ erstellt werden (z. B. Brush, Shader, Path), nicht neu zugewiesen werden müssen.

Alternativ können Sie Objekte auch mit remember außerhalb des Modifikators im Cache speichern. Das ist jedoch nicht immer möglich, da Sie nicht immer Zugriff auf die Komposition haben. Die Verwendung von drawWithCache kann leistungsfähiger sein, wenn die Objekte nur zum Zeichnen verwendet werden.

Wenn Sie beispielsweise ein Brush erstellen, um einen Farbverlauf hinter einem Text zu zeichnen, wird das Brush-Objekt mit drawWithCache im Cache gespeichert, bis sich die Größe des Zeichenbereichs ändert:

Text(
    "Hello Compose!",
    modifier = Modifier
        .drawWithCache {
            val brush = Brush.linearGradient(
                listOf(
                    Color(0xFF9E82F0),
                    Color(0xFF42A5F5)
                )
            )
            onDrawBehind {
                drawRoundRect(
                    brush,
                    cornerRadius = CornerRadius(10.dp.toPx())
                )
            }
        }
)

Brush-Objekt mit drawWithCache im Cache speichern
Abbildung 3: Brush-Objekt mit DrawWithCache im Cache speichern

Grafikmodifikatoren

Modifier.graphicsLayer: Transformationen auf Elemente anwenden

Modifier.graphicsLayer ist ein Modifikator, mit dem der Inhalt des Composeables in eine Zeichenebene gezeichnet wird. Eine Schicht bietet verschiedene Funktionen, z. B.:

  • Isolation der Zeichenanweisungen (ähnlich RenderNode). Zeichenanweisungen, die als Teil einer Ebene erfasst wurden, können von der Renderingpipeline effizient neu ausgegeben werden, ohne Anwendungscode noch einmal ausführen zu müssen.
  • Transformationen, die für alle Zeichenanweisungen in einer Ebene gelten.
  • Rasterisierung für Kompositionsmöglichkeiten Wenn eine Ebene gerastert wird, werden ihre Zeichenanweisungen ausgeführt und die Ausgabe wird in einem Zwischenspeicher außerhalb des Bildschirms erfasst. Das Zusammenstellen eines solchen Zwischenspeichers für nachfolgende Frames ist schneller als das Ausführen der einzelnen Anweisungen. Er verhält sich jedoch wie eine Bitmap, wenn Transformationen wie Skalierung oder Rotation angewendet werden.

Transformation

Modifier.graphicsLayer bietet eine Isolation für die Zeichenanweisungen. So können beispielsweise verschiedene Transformationen mit Modifier.graphicsLayer angewendet werden. Diese können animiert oder geändert werden, ohne dass die Lambda-Zeichnung noch einmal ausgeführt werden muss.

Modifier.graphicsLayer ändert weder die Größe noch die Platzierung des Composeables, da es sich nur auf die Zeichenphase auswirkt. Das bedeutet, dass sich Ihr Composeable über andere Elemente legen kann, wenn es außerhalb seiner Layoutgrenzen gezeichnet wird.

Mit diesem Modifikator können die folgenden Transformationen angewendet werden:

Skalierung – Größe erhöhen

Mit scaleX und scaleY können Sie Inhalte horizontal oder vertikal vergrößern oder verkleinern. Ein Wert von 1.0f bedeutet keine Änderung der Skalierung, ein Wert von 0.5f bedeutet die Hälfte der Dimension.

Image(
    painter = painterResource(id = R.drawable.sunset),
    contentDescription = "Sunset",
    modifier = Modifier
        .graphicsLayer {
            this.scaleX = 1.2f
            this.scaleY = 0.8f
        }
)

Abbildung 4: scaleX und scaleY auf ein Bild-Composeable angewendet
Übersetzung

translationX und translationY können mit graphicsLayer geändert werden. translationX verschiebt die zusammensetzbare Funktion nach links oder rechts. Mit translationY kannst du das Composeable nach oben oder unten verschieben.

Image(
    painter = painterResource(id = R.drawable.sunset),
    contentDescription = "Sunset",
    modifier = Modifier
        .graphicsLayer {
            this.translationX = 100.dp.toPx()
            this.translationY = 10.dp.toPx()
        }
)

Abbildung 5: „translationX“ und „translationY“ werden mit „Modifier.graphicsLayer“ auf das Bild angewendet
Ausrichtung

Legen Sie rotationX für die horizontale, rotationY für die vertikale und rotationZ für die Drehung um die Z‑Achse (Standarddrehung) fest. Dieser Wert wird in Grad (0–360) angegeben.

Image(
    painter = painterResource(id = R.drawable.sunset),
    contentDescription = "Sunset",
    modifier = Modifier
        .graphicsLayer {
            this.rotationX = 90f
            this.rotationY = 275f
            this.rotationZ = 180f
        }
)

Abbildung 6: „rotationX“, „rotationY“ und „rotationZ“ werden für das Bild von „Modifier.graphicsLayer“ festgelegt
Origin

Ein transformOrigin kann angegeben werden. Sie wird dann als Punkt verwendet, von dem aus Transformationen stattfinden. In allen bisherigen Beispielen wurde TransformOrigin.Center verwendet, das sich unter (0.5f, 0.5f) befindet. Wenn Sie den Ursprung bei (0f, 0f) angeben, beginnen die Transformationen in der oberen linken Ecke der zusammensetzbaren Funktion.

Wenn Sie den Ursprung mit einer rotationZ-Transformation ändern, sehen Sie, dass sich das Element oben links im Composeable dreht:

Image(
    painter = painterResource(id = R.drawable.sunset),
    contentDescription = "Sunset",
    modifier = Modifier
        .graphicsLayer {
            this.transformOrigin = TransformOrigin(0f, 0f)
            this.rotationX = 90f
            this.rotationY = 275f
            this.rotationZ = 180f
        }
)

Abbildung 7: Drehung angewendet, TransformOrigin auf 0f, 0f festgelegt

Zuschneiden und Formen

Die Form gibt den Umriss an, an dem der Inhalt abgeschnitten wird, wenn clip = true. In diesem Beispiel haben wir zwei Boxen mit zwei verschiedenen Clips eingerichtet – eine mit der Clipvariablen graphicsLayer und die andere mit dem praktischen Wrapper Modifier.clip.

Column(modifier = Modifier.padding(16.dp)) {
    Box(
        modifier = Modifier
            .size(200.dp)
            .graphicsLayer {
                clip = true
                shape = CircleShape
            }
            .background(Color(0xFFF06292))
    ) {
        Text(
            "Hello Compose",
            style = TextStyle(color = Color.Black, fontSize = 46.sp),
            modifier = Modifier.align(Alignment.Center)
        )
    }
    Box(
        modifier = Modifier
            .size(200.dp)
            .clip(CircleShape)
            .background(Color(0xFF4DB6AC))
    )
}

Der Inhalt des ersten Felds (der Text „Hallo Compose“) wird auf die Kreisform zugeschnitten:

Clip wurde auf zusammensetzbare Box in Box angewendet
Abbildung 8: Clip, der auf das Box-Element angewendet wird

Wenn Sie dann ein translationY auf den oberen rosa Kreis anwenden, sehen Sie, dass die Grenzen des zusammensetzbaren Elements immer noch gleich sind, aber der Kreis unter dem unteren Kreis (und außerhalb seiner Grenzen) gezeichnet wird.

Clip mit „translationY“ angewendet und roter Umriss
Abbildung 9: Clip mit „translationY“ angewendet und roter Umriss

Wenn Sie das Composeable auf den Bereich zuschneiden möchten, in dem es gezeichnet wird, können Sie am Anfang der Modifikatorkette eine weitere Modifier.clip(RectangleShape) hinzufügen. Der Inhalt bleibt dann innerhalb der ursprünglichen Grenzen.

Column(modifier = Modifier.padding(16.dp)) {
    Box(
        modifier = Modifier
            .clip(RectangleShape)
            .size(200.dp)
            .border(2.dp, Color.Black)
            .graphicsLayer {
                clip = true
                shape = CircleShape
                translationY = 50.dp.toPx()
            }
            .background(Color(0xFFF06292))
    ) {
        Text(
            "Hello Compose",
            style = TextStyle(color = Color.Black, fontSize = 46.sp),
            modifier = Modifier.align(Alignment.Center)
        )
    }

    Box(
        modifier = Modifier
            .size(200.dp)
            .clip(RoundedCornerShape(500.dp))
            .background(Color(0xFF4DB6AC))
    )
}

Clip, der auf die Grafikebenentransformation angewendet wird
Abbildung 10: Clip, der auf die Transformation der Grafikebene angewendet wird

Alpha

Mit Modifier.graphicsLayer kann eine alpha (Deckkraft) für die gesamte Ebene festgelegt werden. 1.0f ist vollständig undurchsichtig und 0.0f ist unsichtbar.

Image(
    painter = painterResource(id = R.drawable.sunset),
    contentDescription = "clock",
    modifier = Modifier
        .graphicsLayer {
            this.alpha = 0.5f
        }
)

Bild mit angewendetem Alphatest
Abbildung 11: Bild mit angewendetem Alpha

Compositing-Strategie

Die Arbeit mit Alpha und Transparenz ist möglicherweise nicht so einfach wie das Ändern eines einzelnen Alphawerts. Neben der Änderung einer Alphaversion gibt es auch die Möglichkeit, ein CompositingStrategy für eine graphicsLayer festzulegen. Mit einer CompositingStrategy wird festgelegt, wie die Inhalte des Composeables mit den anderen Inhalten kombiniert werden, die bereits auf dem Bildschirm dargestellt werden.

Die verschiedenen Strategien sind:

Automatisch (Standard)

Die Kompositionsstrategie wird durch die übrigen graphicsLayer-Parameter bestimmt. Die Ebene wird in einen Offscreen-Puffer gerendert, wenn der Alphawert kleiner als 1,0 f ist oder RenderEffect festgelegt ist. Wenn der Alphawert unter 1f liegt, wird automatisch eine Kompositionierungsebene erstellt, um den Inhalt zu rendern und dann diesen Offscreen-Puffer mit dem entsprechenden Alphawert an das Ziel zu zeichnen. Wenn du RenderEffect oder Overscroll festlegst, werden Inhalte unabhängig von der festgelegten CompositingStrategy immer in einem Offscreen-Puffer gerendert.

Nicht sichtbar

Der Inhalt des Composeables wird immer in eine Offscreen-Textur oder -Bitmap gerastert, bevor er an das Ziel gerendert wird. Das ist nützlich, um BlendMode-Vorgänge zum Maskieren von Inhalten anzuwenden und die Leistung beim Rendern komplexer Zeichenanweisungen zu verbessern.

Ein Beispiel für die Verwendung von CompositingStrategy.Offscreen ist BlendModes. Nehmen wir an, Sie möchten Teile einer zusammensetzbaren Image-Funktion entfernen, indem Sie einen Zeichenbefehl ausführen, der BlendMode.Clear verwendet. Wenn Sie compositingStrategy nicht auf CompositingStrategy.Offscreen setzen, interagiert BlendMode mit allen darunter liegenden Inhalten.

Image(
    painter = painterResource(id = R.drawable.dog),
    contentDescription = "Dog",
    contentScale = ContentScale.Crop,
    modifier = Modifier
        .size(120.dp)
        .aspectRatio(1f)
        .background(
            Brush.linearGradient(
                listOf(
                    Color(0xFFC5E1A5),
                    Color(0xFF80DEEA)
                )
            )
        )
        .padding(8.dp)
        .graphicsLayer {
            compositingStrategy = CompositingStrategy.Offscreen
        }
        .drawWithCache {
            val path = Path()
            path.addOval(
                Rect(
                    topLeft = Offset.Zero,
                    bottomRight = Offset(size.width, size.height)
                )
            )
            onDrawWithContent {
                clipPath(path) {
                    // this draws the actual image - if you don't call drawContent, it wont
                    // render anything
                    this@onDrawWithContent.drawContent()
                }
                val dotSize = size.width / 8f
                // Clip a white border for the content
                drawCircle(
                    Color.Black,
                    radius = dotSize,
                    center = Offset(
                        x = size.width - dotSize,
                        y = size.height - dotSize
                    ),
                    blendMode = BlendMode.Clear
                )
                // draw the red circle indication
                drawCircle(
                    Color(0xFFEF5350), radius = dotSize * 0.8f,
                    center = Offset(
                        x = size.width - dotSize,
                        y = size.height - dotSize
                    )
                )
            }
        }
)

Wenn du CompositingStrategy auf Offscreen setzt, wird eine Offscreen-Textur erstellt, auf die die Befehle ausgeführt werden. BlendMode wird dabei nur auf den Inhalt dieses Composeables angewendet. Dieser wird dann über dem bereits gerenderten Inhalt auf dem Bildschirm gerendert, ohne dass sich dies auf den bereits gezeichneten Inhalt auswirkt.

Modifier.drawWithContent auf einem Bild mit einer Kreisangabe, mit BlendMode.Clear in der App
Abbildung 12: Modifier.drawWithContent auf einem Bild mit einem Kreis, mit BlendMode.Clear und CompositingStrategy.Offscreen in der App

Wenn Sie CompositingStrategy.Offscreen nicht verwendet haben, werden durch die Anwendung von BlendMode.Clear alle Pixel im Ziel gelöscht, unabhängig davon, was bereits festgelegt wurde. Der Rendering-Puffer des Fensters (schwarz) bleibt sichtbar. Viele der BlendModes-Funktionen, die Alpha beinhalten, funktionieren ohne Offscreen-Puffer nicht wie erwartet. Beachten Sie den schwarzen Ring um den roten Kreis:

Modifier.drawWithContent auf einem Bild mit einem Kreissymbol, mit BlendMode.Clear und ohne festgelegte CompositingStrategy
Abbildung 13: Modifier.drawWithContent auf einem Bild mit einem Kreis, BlendMode.Clear und keine CompositingStrategy festgelegt

Zum besseren Verständnis: Wenn die App einen durchscheinenden Fensterhintergrund hätte und du CompositingStrategy.Offscreen nicht verwendet hast, interagiert BlendMode mit der gesamten App. Es werden alle Pixel gelöscht, um die App oder den Hintergrund darunter anzuzeigen, wie in diesem Beispiel:

Es wurde keine CompositingStrategy festgelegt und BlendMode.Clear wird mit einer App mit einem halbtransparenten Fensterhintergrund verwendet. Die rosa Tapete ist im Bereich um den roten Statuskreis zu sehen.
Abbildung 14: Es ist keine CompositingStrategy festgelegt und BlendMode.Clear wird mit einer App mit einem halbtransparenten Fensterhintergrund verwendet. Beachten Sie, dass der rosafarbene Hintergrund im Bereich um den roten Statuskreis herum zu sehen ist.

Hinweis: Wenn Sie CompositingStrategy.Offscreen verwenden, wird eine Offscreen-Textur erstellt, die der Größe des Zeichenbereichs entspricht, und wieder auf dem Bildschirm gerendert. Alle Zeichnungsbefehle, die mit dieser Strategie ausgeführt werden, werden standardmäßig auf diese Region zugeschnitten. Das folgende Code-Snippet veranschaulicht die Unterschiede beim Wechsel zu Offscreen-Texturen:

@Composable
fun CompositingStrategyExamples() {
    Column(
        modifier = Modifier
            .fillMaxSize()
            .wrapContentSize(Alignment.Center)
    ) {
        // Does not clip content even with a graphics layer usage here. By default, graphicsLayer
        // does not allocate + rasterize content into a separate layer but instead is used
        // for isolation. That is draw invalidations made outside of this graphicsLayer will not
        // re-record the drawing instructions in this composable as they have not changed
        Canvas(
            modifier = Modifier
                .graphicsLayer()
                .size(100.dp) // Note size of 100 dp here
                .border(2.dp, color = Color.Blue)
        ) {
            // ... and drawing a size of 200 dp here outside the bounds
            drawRect(color = Color.Magenta, size = Size(200.dp.toPx(), 200.dp.toPx()))
        }

        Spacer(modifier = Modifier.size(300.dp))

        /* Clips content as alpha usage here creates an offscreen buffer to rasterize content
        into first then draws to the original destination */
        Canvas(
            modifier = Modifier
                // force to an offscreen buffer
                .graphicsLayer(compositingStrategy = CompositingStrategy.Offscreen)
                .size(100.dp) // Note size of 100 dp here
                .border(2.dp, color = Color.Blue)
        ) {
            /* ... and drawing a size of 200 dp. However, because of the CompositingStrategy.Offscreen usage above, the
            content gets clipped */
            drawRect(color = Color.Red, size = Size(200.dp.toPx(), 200.dp.toPx()))
        }
    }
}

CompositingStrategy.Auto im Vergleich zu CompositingStrategy.Offscreen: Clips, die nicht im Bild sind, werden in die Region eingefügt, bei „Auto“ ist das nicht der Fall.
Abbildung 15: „CompositingStrategy.Auto“ im Vergleich zu „CompositingStrategy.Offscreen“ – Clips, die nicht im Bild sind, werden in die Region eingefügt, während dies bei „Auto“ nicht der Fall ist
ModulateAlpha

Bei dieser Kompositionsstrategie wird der Alphawert für jede der im graphicsLayer aufgezeichneten Zeichenanweisungen moduliert. Es wird kein Offscreen-Zwischenspeicher für Alphaversionen unter 1,0f erstellt, es sei denn, RenderEffect ist festgelegt, sodass er beim Alpha-Rendering effizienter sein kann. Bei sich überschneidenden Inhalten kann es jedoch zu unterschiedlichen Ergebnissen kommen. Bei Anwendungsfällen, bei denen im Voraus bekannt ist, dass sich Inhalte nicht überschneiden, kann dies eine bessere Leistung als CompositingStrategy.Auto mit Alphawerten unter 1 bieten.

Unten sehen Sie ein weiteres Beispiel für verschiedene Kompositionsstrategien: Unterschiedliche Alphas werden auf verschiedene Teile der Composeables angewendet und eine Modulate-Strategie wird verwendet:

@Preview
@Composable
fun CompositingStrategy_ModulateAlpha() {
    Column(
        modifier = Modifier
            .fillMaxSize()
            .padding(32.dp)
    ) {
        // Base drawing, no alpha applied
        Canvas(
            modifier = Modifier.size(200.dp)
        ) {
            drawSquares()
        }

        Spacer(modifier = Modifier.size(36.dp))

        // Alpha 0.5f applied to whole composable
        Canvas(
            modifier = Modifier
                .size(200.dp)
                .graphicsLayer {
                    alpha = 0.5f
                }
        ) {
            drawSquares()
        }
        Spacer(modifier = Modifier.size(36.dp))

        // 0.75f alpha applied to each draw call when using ModulateAlpha
        Canvas(
            modifier = Modifier
                .size(200.dp)
                .graphicsLayer {
                    compositingStrategy = CompositingStrategy.ModulateAlpha
                    alpha = 0.75f
                }
        ) {
            drawSquares()
        }
    }
}

private fun DrawScope.drawSquares() {

    val size = Size(100.dp.toPx(), 100.dp.toPx())
    drawRect(color = Red, size = size)
    drawRect(
        color = Purple, size = size,
        topLeft = Offset(size.width / 4f, size.height / 4f)
    )
    drawRect(
        color = Yellow, size = size,
        topLeft = Offset(size.width / 4f * 2f, size.height / 4f * 2f)
    )
}

val Purple = Color(0xFF7E57C2)
val Yellow = Color(0xFFFFCA28)
val Red = Color(0xFFEF5350)

ModulateAlpha wendet den Alpha-Satz auf jeden einzelnen Zeichenbefehl an
Abbildung 16: ModulateAlpha wendet den Alphasatz auf jeden einzelnen Zeichenbefehl an.

Inhalt eines Composeables in eine Bitmap schreiben

Ein häufiger Anwendungsfall ist das Erstellen einer Bitmap aus einem Composeable. Wenn Sie den Inhalt der zusammensetzbaren Funktion in ein Bitmap kopieren möchten, erstellen Sie mit rememberGraphicsLayer() eine GraphicsLayer.

Leiten Sie die Zeichenbefehle mithilfe von drawWithContent() und graphicsLayer.record{} an die neue Ebene weiter. Zeichnen Sie dann die Ebene mit drawLayer auf dem sichtbaren Canvas:

val coroutineScope = rememberCoroutineScope()
val graphicsLayer = rememberGraphicsLayer()
Box(
    modifier = Modifier
        .drawWithContent {
            // call record to capture the content in the graphics layer
            graphicsLayer.record {
                // draw the contents of the composable into the graphics layer
                this@drawWithContent.drawContent()
            }
            // draw the graphics layer on the visible canvas
            drawLayer(graphicsLayer)
        }
        .clickable {
            coroutineScope.launch {
                val bitmap = graphicsLayer.toImageBitmap()
                // do something with the newly acquired bitmap
            }
        }
        .background(Color.White)
) {
    Text("Hello Android", fontSize = 26.sp)
}

Du kannst die Bitmap auf der Festplatte speichern und sie teilen. Weitere Informationen finden Sie im vollständigen Beispiel-Snippet. Prüfen Sie, ob Sie die erforderlichen Berechtigungen auf dem Gerät haben, bevor Sie versuchen, auf dem Laufwerk zu speichern.

Modifikator für benutzerdefinierte Zeichnungen

Wenn Sie einen eigenen benutzerdefinierten Modifikator erstellen möchten, implementieren Sie die DrawModifier-Schnittstelle. So erhalten Sie Zugriff auf einen ContentDrawScope, der mit dem identisch ist, der bei Verwendung von Modifier.drawWithContent() freigegeben wird. Sie können dann häufig verwendete Zeichenvorgänge in benutzerdefinierte Zeichenmodifikatoren extrahieren, um den Code zu bereinigen und praktische Wrapper bereitzustellen. Modifier.background() ist beispielsweise eine praktische DrawModifier.

Wenn Sie beispielsweise eine Modifier implementieren möchten, die Inhalte vertikal dreht, können Sie sie so erstellen:

class FlippedModifier : DrawModifier {
    override fun ContentDrawScope.draw() {
        scale(1f, -1f) {
            this@draw.drawContent()
        }
    }
}

fun Modifier.flipped() = this.then(FlippedModifier())

Verwenden Sie dann diesen umgekehrten Modifikator auf Text:

Text(
    "Hello Compose!",
    modifier = Modifier
        .flipped()
)

Benutzerdefinierter gespiegelter Modifier für Text
Abbildung 17: Benutzerdefinierter gespiegelter Modifier für Text

Weitere Informationen

Weitere Beispiele für die Verwendung von graphicsLayer und benutzerdefinierten Zeichnungen finden Sie in den folgenden Ressourcen: