Grafikmodifikatoren

Zusätzlich zur zusammensetzbaren Funktion Canvas bietet Compose mehrere nützliche Modifiers für Grafiken, die beim Zeichnen benutzerdefinierter Inhalte helfen. Diese Modifikatoren sind nützlich, da sie auf jedes Composable angewendet werden können.

Zeichnungsmodifikatoren

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

Der Basismodifikator für das Zeichnen ist drawWithContent. Damit können Sie die Zeichenreihenfolge Ihrer Composable und die Zeichenbefehle festlegen, die im Modifikator ausgegeben werden. drawBehind ist ein praktischer Wrapper um drawWithContent, bei dem die Zeichenreihenfolge auf „hinter dem Inhalt des Composables“ festgelegt ist. drawWithCache ruft entweder onDrawBehind oder onDrawWithContent auf und bietet einen Mechanismus zum Zwischenspeichern der darin erstellten Objekte.

Modifier.drawWithContent: Zeichenreihenfolge auswählen

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

Wenn Sie beispielsweise einen radialen Farbverlauf über Ihren Inhalten rendern möchten, um einen Schlüsselloch-Effekt auf der Benutzeroberfläche zu erzeugen, können Sie Folgendes tun:

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 oben auf einer Composable-Funktion verwendet, um eine Taschenlampen-Benutzeroberfläche zu erstellen.

Modifier.drawBehind: Zeichnung hinter einem Composable

Mit Modifier.drawBehind können Sie DrawScope-Vorgänge hinter den Composable-Inhalten ausführen, die auf dem Bildschirm dargestellt werden. Wenn Sie sich die Implementierung von Canvas ansehen, werden Sie feststellen, dass es sich nur um einen praktischen Wrapper für Modifier.drawBehind handelt.

So zeichnen Sie ein abgerundetes Rechteck hinter Text:

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 ein mit Modifier.drawBehind gezeichneter Hintergrund
Abbildung 2: Text und ein Hintergrund, die mit Modifier.drawBehind gezeichnet wurden

Modifier.drawWithCache: Ziehungsobjekte zeichnen und im Cache speichern

Mit Modifier.drawWithCache werden die darin erstellten Objekte im Cache gespeichert. Die Objekte werden so lange im Cache gespeichert, wie die Größe des Zeichenbereichs gleich ist oder sich keine der gelesenen Statusobjekte geändert hat. Dieser Modifikator ist nützlich, um die Leistung von Zeichenaufrufen zu verbessern, da er die Notwendigkeit vermeidet, Objekte (z. B. Brush, Shader, Path usw.) neu zuzuweisen, die beim Zeichnen erstellt werden.

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 zwischengespeichert, 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())
                )
            }
        }
)

Pinselobjekt mit „drawWithCache“ im Cache speichern
Abbildung 3: Zwischenspeichern des Brush-Objekts mit drawWithCache

Grafikmodifikatoren

Modifier.graphicsLayer: Transformationen auf Composables anwenden

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

  • Isolierung für die Zeichenanweisungen (ähnlich wie bei RenderNode): Zeichenanweisungen, die als Teil einer Ebene erfasst werden, können von der Rendering-Pipeline effizient neu ausgegeben werden, ohne dass Anwendungscode neu ausgeführt werden muss.
  • Transformationen, die auf alle Zeichenanweisungen in einer Ebene angewendet werden.
  • Rasterung für Kompositionsfunktionen. Wenn eine Ebene gerastert wird, werden ihre Zeichenanweisungen ausgeführt und die Ausgabe wird in einem Offscreen-Puffer erfasst. Das Zusammensetzen eines solchen Puffers für nachfolgende Frames ist schneller als die Ausführung der einzelnen Anweisungen. Bei Transformationen wie Skalierung oder Drehung verhält er sich jedoch wie eine Bitmap.

Transformationen

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

Modifier.graphicsLayer ändert die gemessene Größe oder Position Ihres Composables nicht, da es sich nur auf die Zeichenphase auswirkt. Das bedeutet, dass sich Ihre zusammensetzbare Funktion möglicherweise mit anderen überschneidet, wenn sie außerhalb ihrer Layoutgrenzen gerendert wird.

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

Skalieren – Größe erhöhen

Mit scaleX und scaleY wird der Inhalt in horizontaler bzw. vertikaler Richtung vergrößert oder verkleinert. Ein Wert von 1.0f bedeutet keine Änderung des Maßstabs, ein Wert von 0.5f 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 eine Image-Composable angewendet
Übersetzung

translationX und translationY können mit graphicsLayer geändert werden. translationX verschiebt das Composable nach links oder rechts. Mit translationY wird das Composable nach oben oder unten verschoben.

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“ auf „Image“ mit „Modifier.graphicsLayer“ angewendet
Ausrichtung

Legen Sie rotationX für die horizontale Drehung, rotationY für die vertikale Drehung und rotationZ für die Drehung auf der 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 für Bild durch Modifier.graphicsLayer festgelegt
Origin

Ein transformOrigin kann angegeben werden. Er wird dann als Ausgangspunkt für Transformationen verwendet. 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 linken oberen Ecke der Composable.

Wenn Sie den Ursprung mit einer rotationZ-Transformation ändern, dreht sich das Element um die obere linke Ecke des Composables:

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: Rotation mit TransformOrigin auf 0f, 0f gesetzt

Clip und Form

Mit „Form“ wird der Umriss angegeben, an den der Inhalt angelegt wird, wenn clip = true. In diesem Beispiel legen wir für zwei Kästen zwei verschiedene Clips fest – einen mit der Clip-Variablen graphicsLayer und den anderen 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 „Hello Compose“) wird auf die Kreisform zugeschnitten:

Clip auf Box-Composable angewendet
Abbildung 8: Auf Box-Composable angewendeter Clip

Wenn Sie dann ein translationY auf den oberen rosafarbenen Kreis anwenden, sehen Sie, dass die Grenzen der Composable weiterhin gleich sind, der Kreis jedoch unter dem unteren Kreis (und außerhalb seiner Grenzen) gezeichnet wird.

Clip mit angewendeter „translationY“-Eigenschaft und rotem Rahmen für die Kontur
Abbildung 9: Clip mit angewendeter TranslationY und rotem Rahmen für die Kontur

Wenn Sie das Composable auf die Region beschränken möchten, in der es gezeichnet wird, können Sie am Anfang der Modifier-Kette ein weiteres Modifier.clip(RectangleShape) hinzufügen. Die Inhalte bleiben 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 zusätzlich zur Transformation der GraphicsLayer angewendet wird
Abbildung 10: Clip, der auf die graphicsLayer-Transformation 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 Alphakanal
Abbildung 11: Bild mit angewendetem Alphawert

Compositing-Strategie

Das Arbeiten mit Alpha und Transparenz ist möglicherweise nicht so einfach wie das Ändern eines einzelnen Alphawerts. Neben der Änderung eines Alpha-Werts besteht auch die Möglichkeit, einen CompositingStrategy für einen graphicsLayer festzulegen. Eine CompositingStrategy bestimmt, wie der Inhalt der Composable mit den anderen Inhalten, die bereits auf dem Bildschirm gezeichnet wurden, zusammengesetzt wird.

Folgende Strategien sind verfügbar:

Automatisch (Standard)

Die Compositing-Strategie wird durch die restlichen graphicsLayer-Parameter bestimmt. Die Ebene wird in einem Offscreen-Puffer gerendert, wenn der Alphawert kleiner als 1,0 f ist oder ein RenderEffect festgelegt ist. Wenn der Alphawert kleiner als 1f ist, wird automatisch eine Compositing-Ebene erstellt, um den Inhalt zu rendern und diesen Offscreen-Puffer dann mit dem entsprechenden Alphawert auf das Ziel zu zeichnen. Wenn Sie RenderEffect oder Overscroll festlegen, werden Inhalte immer in einem Offscreen-Puffer gerendert, unabhängig von der Einstellung für CompositingStrategy.

Nicht sichtbar

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

Ein Beispiel für die Verwendung von CompositingStrategy.Offscreen ist BlendModes. Angenommen, Sie möchten im folgenden Beispiel Teile einer Image-Composable-Funktion entfernen, indem Sie einen Zeichenbefehl mit BlendMode.Clear ausgeben. Wenn Sie compositingStrategy nicht auf CompositingStrategy.Offscreen festlegen, interagiert BlendMode mit allen Inhalten darunter.

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 Sie CompositingStrategy auf Offscreen setzen, wird eine Offscreen-Textur erstellt, in der die Befehle ausgeführt werden (BlendMode wird nur auf den Inhalt dieses Composables angewendet). Es wird dann über dem gerendert, was bereits auf dem Bildschirm gerendert wurde, ohne die bereits gezeichneten Inhalte zu beeinträchtigen.

Modifier.drawWithContent für ein Bild mit einem Kreis, der BlendMode.Clear in der App zeigt
Abbildung 12: Modifier.drawWithContent für ein Bild mit einem Kreis, der auf BlendMode.Clear und CompositingStrategy.Offscreen in der App hinweist

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 war. Der Rendering-Puffer des Fensters (schwarz) bleibt sichtbar. Viele BlendModes, bei denen der Alphakanal verwendet wird, funktionieren ohne Offscreen-Puffer nicht wie erwartet. Achten Sie auf den schwarzen Ring um die rote Kreisanzeige:

Modifier.drawWithContent für ein Bild mit einem Kreis, der BlendMode.Clear verwendet und für den keine CompositingStrategy festgelegt ist
Abbildung 13: Modifier.drawWithContent für ein Bild mit einem Kreis, der mit BlendMode.Clear gezeichnet wird und für den keine CompositingStrategy festgelegt ist

Wenn die App einen durchscheinenden Fensterhintergrund hatte und Sie CompositingStrategy.Offscreen nicht verwendet haben, interagiert BlendMode mit der gesamten App. Dabei werden alle Pixel gelöscht, um die darunter liegende App oder das Hintergrundbild anzuzeigen, wie in diesem Beispiel:

Es wurde keine CompositingStrategy festgelegt und BlendMode.Clear wird mit einer App verwendet, die einen durchscheinenden Fensterhintergrund hat. Die rosa Tapete ist im Bereich um den roten Statuskreis zu sehen.
Abbildung 14: Keine CompositingStrategy festgelegt und BlendMode.Clear mit einer App mit einem durchscheinenden Fensterhintergrund verwendet. Die pinkfarbene Tapete ist im Bereich um den roten Statuskreis zu sehen.

Wenn Sie CompositingStrategy.Offscreen verwenden, wird eine Offscreen-Textur in der Größe des Zeichenbereichs erstellt und auf dem Bildschirm gerendert. Alle Zeichenbefehle, die mit dieser Strategie ausgeführt werden, werden standardmäßig auf diese Region zugeschnitten. Das folgende Code-Snippet veranschaulicht die Unterschiede beim Umstellen auf die Verwendung von 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: Offscreen-Clips werden auf die Region beschränkt, in der „Auto“ nicht funktioniert.
Abbildung 15: CompositingStrategy.Auto im Vergleich zu CompositingStrategy.Offscreen – Offscreen-Clips in der Region, in der „Auto“ nicht funktioniert
ModulateAlpha

Diese Kompositionsstrategie moduliert den Alphawert für jede der in graphicsLayer aufgezeichneten Zeichenanweisungen. Es wird kein Offscreen-Puffer für Alpha unter 1,0 f erstellt, sofern kein RenderEffect festgelegt ist.Das kann die Alpha-Wiedergabe effizienter machen. Bei sich überschneidenden Inhalten können jedoch unterschiedliche Ergebnisse erzielt werden. In Anwendungsfällen, in denen im Voraus bekannt ist, dass sich Inhalte nicht überschneiden, kann dies eine bessere Leistung als CompositingStrategy.Auto mit Alphawerten unter 1 bieten.

Ein weiteres Beispiel für verschiedene Kompositionsstrategien sehen Sie unten. Hier werden unterschiedliche Alphas auf verschiedene Teile der Composables angewendet und eine Modulate-Strategie 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)

Mit „ModulateAlpha“ wird der für jeden einzelnen Zeichenbefehl festgelegte Alphawert angepasst.
Abbildung 16: „ModulateAlpha“ wendet den für jeden einzelnen Zeichenbefehl festgelegten Alphawert an.

Inhalt einer Composable-Funktion in eine Bitmap schreiben

Ein häufiger Anwendungsfall ist das Erstellen eines Bitmap aus einer Composable-Funktion. Wenn Sie den Inhalt Ihres Composables in ein Bitmap kopieren möchten, erstellen Sie mit rememberGraphicsLayer() ein GraphicsLayer.

Leiten Sie die Zeichenbefehle mit drawWithContent() und graphicsLayer.record{} an die neue Ebene weiter. Zeichnen Sie die Ebene dann 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)
}

Sie können die Bitmap auf der Festplatte speichern und freigeben. Weitere Informationen finden Sie im vollständigen Beispiel-Snippet. Prüfen Sie, ob Berechtigungen auf dem Gerät vorhanden sind, bevor Sie versuchen, auf der Festplatte zu speichern.

Modifikator für benutzerdefinierte Zeichnungen

Wenn Sie einen eigenen benutzerdefinierten Modifier erstellen möchten, implementieren Sie die DrawModifier-Schnittstelle. Dadurch erhalten Sie Zugriff auf ein ContentDrawScope, das dem entspricht, was bei Verwendung von Modifier.drawWithContent() verfügbar ist. Sie können dann allgemeine Zeichenvorgänge in benutzerdefinierte Zeichenmodifikatoren extrahieren, um den Code zu bereinigen und praktische Wrapper bereitzustellen. Modifier.background() ist beispielsweise ein praktischer DrawModifier.

Wenn Sie beispielsweise einen Modifier implementieren möchten, der Inhalte vertikal spiegelt, können Sie ihn 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 für Text:

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

Benutzerdefinierter Modifier „Gedreht“ für Text
Abbildung 17: Benutzerdefinierter Flipped-Modifier für Text

Zusätzliche Ressourcen

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