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 Composeable 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, bei dem die Zeichenreihenfolge hinter dem Inhalt des Composeables festgelegt ist. drawWithCache ruft entweder onDrawBehind oder onDrawWithContent auf und bietet einen Mechanismus zum Caching der darin erstellten Objekte.

Modifier.drawWithContent: Zeichenreihenfolge 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 Ihre Inhalte vor oder nach Ihren benutzerdefinierten Zeichenvorgängen gezeichnet werden sollen.

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 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 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: Cachen des Brush-Objekts mit drawWithCache

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.:

  • Isolierung für die Zeichenanweisungen (ähnlich wie bei RenderNode). Zeichenanweisungen, die als Teil einer Ebene erfasst wurden, können von der Rendering-Pipeline effizient neu ausgegeben werden, ohne dass der Anwendungscode noch einmal ausgeführt werden muss.
  • Transformationen, die auf alle Zeichnungsanweisungen in einer Ebene angewendet werden.
  • Rasterisierung für Kompositionsmöglichkeiten Wenn eine Ebene gerastert wird, werden die zugehörigen Zeichenanweisungen ausgeführt und die Ausgabe wird in einem Offscreen-Puffer erfasst. Das Compositing eines solchen Buffers für nachfolgende Frames ist schneller als die Ausführung der einzelnen Anweisungen. Er verhält sich jedoch wie eine Bitmap, wenn Transformationen wie Skalierung oder Drehung angewendet werden.

Transformation

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

Modifier.graphicsLayer ändert weder die Größe noch die Platzierung des Composeables, da es sich nur auf die Zeichnenphase 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 durch graphicsLayer ersetzt werden. Mit translationX wird das Element nach links oder rechts verschoben. 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 eine horizontale, rotationY für eine vertikale und rotationZ für eine 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. 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 links oben im Composeable.

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

Mit „Shape“ (Form) wird der Umriss angegeben, an den die Inhalte zugeschnitten werden, 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 auf Box-Komposition angewendet
Abbildung 8: Clip, der auf das Box-Element angewendet wird

Wenn Sie dann eine translationY auf den oberen rosa Kreis anwenden, sehen Sie, dass die Begrenzungen des Composeables gleich bleiben, der Kreis aber unter dem unteren Kreis (und außerhalb seiner Begrenzung) 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 können Sie eine alpha (Deckkraft) für die gesamte Ebene festlegen. 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 Alpha

Compositing-Strategie

Die Arbeit mit Alpha und Transparenz ist möglicherweise nicht so einfach wie das Ändern eines einzelnen Alphawerts. Neben der Änderung eines Alphakanals können Sie auch einen CompositingStrategy für einen graphicsLayer festlegen. 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. Im folgenden Beispiel möchten Sie Teile einer Image-Komposition entfernen, indem Sie einen Zeichenbefehl mit BlendMode.Clear ausführen. Wenn Sie compositingStrategy nicht auf CompositingStrategy.Offscreen festlegen, interagiert BlendMode mit dem gesamten Inhalt 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 du CompositingStrategy auf Offscreen setzt, wird eine Offscreen-Textur erstellt, auf die die Befehle ausgeführt werden. BlendMode wird dann 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 Kreismarkierung, 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

Zur Verdeutlichung: Wenn die App einen halbtransparenten Fensterhintergrund hat und Sie die CompositingStrategy.Offscreen nicht verwenden, interagiert die BlendMode mit der gesamten App. Alle Pixel werden 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-Puffer für Alphawerte unter 1.0f erstellt, es sei denn, RenderEffect ist festgelegt. Das kann für das Alpha-Rendering effizienter sein. 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)

Mit ModulateAlpha wird der Alphasatz auf jeden einzelnen Zeichenbefehl angewendet.
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 Ihres Composeables 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 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)
}

Sie können die Bitmap auf dem Laufwerk speichern und freigeben. Weitere Informationen finden Sie im vollständigen Beispiel-Snippet. Prüfen Sie die Berechtigungen auf dem Gerät, 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: