Grafikmodifikatoren

Zusätzlich zu den zusammensetzbaren Canvas-Funktionen gibt es in Compose mehrere nützliche Grafiken Modifiers, die Sie beim Zeichnen benutzerdefinierter Inhalte unterstützen. Diese Modifikatoren sind nützlich, da sie auf jede zusammensetzbare Funktion angewendet werden können.

Zeichenmodifikatoren

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

Der Basismodifikator für das Zeichnen ist drawWithContent. Damit können Sie die Zeichenreihenfolge des kompilierbaren Elements und die Zeichenbefehle 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 darin 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 der zusammensetzbaren Funktion ausführen. Rufen Sie unbedingt drawContent auf, um den eigentlichen Inhalt der zusammensetzbaren Funktion 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 Inhalten rendern möchten, um auf der Benutzeroberfläche einen Taschenlampen-Keyhole-Effekt zu erzeugen, können Sie so vorgehen:

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 auf einem Composable verwendet, um eine UI für Taschenlampen zu erstellen.

Modifier.drawBehind: Zeichnung hinter einer zusammensetzbaren Funktion

Mit Modifier.drawBehind können Sie DrawScope-Vorgänge für zusammensetzbare Inhalte ausführen, die auf dem Bildschirm gezeichnet werden. Wenn Sie sich die Implementierung von Canvas ansehen, werden Sie feststellen, dass es sich nur um einen praktischen Wrapper um 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 mit Modifier.drawBehind gezeichneter Hintergrund

Modifier.drawWithCache: Zeichenobjekte zeichnen und im Cache speichern

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

Alternativ können Sie Objekte auch mit remember außerhalb des Modifikators im Cache speichern. Dies ist jedoch nicht immer möglich, da Sie nicht immer Zugriff auf die Komposition haben. drawWithCache ist möglicherweise leistungsstärker, 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 mitdrawWithCache im Cache speichern
Abbildung 3: Pinsel-Objekt mitdrawWithCache im Cache speichern

Grafikmodifikatoren

Modifier.graphicsLayer: Transformationen auf zusammensetzbare Funktionen anwenden

Modifier.graphicsLayer ist ein Modifikator, mit dem der Inhalt der zusammensetzbaren Zeichnung zu einer Zeichenebene wird. Eine Ebene 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.
  • Rasterung für Kompositionsfunktionen. 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 Isolierung der Zeichenanweisungen. Beispielsweise können 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 nicht die gemessene Größe oder Position der zusammensetzbaren Funktion, da sie sich nur auf die Zeichenphase auswirkt. Dies bedeutet, dass Ihre zusammensetzbare Funktion andere überlappen kann, wenn sie außerhalb ihrer Layoutgrenzen gezeichnet wird.

Die folgenden Transformationen können mit diesem Modifikator angewendet werden:

Skalieren – Größe erhöhen

Mit scaleX und scaleY wird der Inhalt in horizontaler bzw. vertikaler Richtung vergrößert oder verkleinert. Der Wert 1.0f bedeutet, dass die Skalierung nicht geändert wurde, 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" angewendet auf eine zusammensetzbare Funktion des Bildes
Übersetzung

translationX und translationY können mit graphicsLayer geändert werden. translationX verschiebt die zusammensetzbare Funktion nach links oder rechts. translationY verschiebt die zusammensetzbare Funktion nach oben oder unten.

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 mit Modifier.graphicsLayer auf ein Bild angewendet
Drehung

Stellen Sie rotationX für eine horizontale Drehung, rotationY für eine vertikale Drehung und rotationZ für eine Drehung um die Z-Achse (Standarddrehung) ein. 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 durch Modifier.graphicsLayer für das Bild festgelegt
Origin

Ein transformOrigin kann angegeben werden. Sie wird dann als Punkt verwendet, von dem aus Transformationen stattfinden. Bei allen bisherigen Beispielen wurde TransformOrigin.Center verwendet, der sich bei (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 um die obere linke Ecke der zusammensetzbaren Funktion 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: Rotation angewendet, wobei TransformOrigin auf 0f, 0f festgelegt ist

Clip und Form

Die Form gibt den Umriss an, an dem der Inhalt abgeschnitten wird, wenn clip = true. In diesem Beispiel legen wir zwei Kästchen mit zwei verschiedenen Clips fest – eines mit der Clip-Variable graphicsLayer und das 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 „Hello Compose“) wird auf die Kreisform zugeschnitten:

Clip auf zusammensetzbare Box in Box angewendet
Abbildung 8: Clip auf zusammensetzbare Box angewendet

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

Der Clip wurde mit der Übersetzung Y und dem roten Rahmen als Umriss angewendet.
Abbildung 9: Clip mit Umsetzung Y und roter Umriss

Um die zusammensetzbare Funktion auf den Bereich zu beschränken, in dem sie gezeichnet wurde, können Sie am Anfang der Modifikatorkette ein weiteres Modifier.clip(RectangleShape)-Element 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 wird auf „graphicLayer“-Transformation angewendet
Abbildung 10: Clip wird auf „graphicLayer“-Transformation angewendet

Alpha

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

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

Bild mit angewendetem Alphatest
Abbildung 11: Bild mit Alphatest

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. Ein CompositingStrategy bestimmt, wie der Inhalt der zusammensetzbaren Funktion mit den anderen bereits auf dem Bildschirm gezeichneten Inhalten zusammengesetzt wird.

Die verschiedenen Strategien sind:

Automatisch (Standard)

Die Zusammensetzungsstrategie wird durch den Rest der graphicsLayer-Parameter bestimmt. Die Ebene wird in einen Zwischenspeicher außerhalb des Bildschirms gerendert, wenn der Alphawert unter 1.0f liegt oder ein RenderEffect festgelegt ist. Immer wenn der Alphawert kleiner als 1f ist, wird automatisch eine Compositing-Ebene erstellt, um den Inhalt zu rendern und dann diesen Offscreen-Zwischenspeicher mit dem entsprechenden Alphakanal in das Ziel zu ziehen. Wenn Sie RenderEffect oder Overscroll festlegen, werden Inhalte unabhängig vom festgelegten CompositingStrategy immer in einem Zwischenspeicher außerhalb des Bildschirms gerendert.

Nicht sichtbar

Der Inhalt der zusammensetzbaren Funktion wird immer auf einer nicht sichtbaren Textur oder Bitmap gerastert, bevor sie im Ziel gerendert werden. Dies ist nützlich, um BlendMode-Vorgänge zum Maskieren von Inhalten anzuwenden und beim Rendern komplexer Gruppen von Zeichenanweisungen die Leistung 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 Sie CompositingStrategy auf Offscreen setzen, wird eine nicht sichtbare Textur erstellt, auf die die Befehle ausgeführt werden. Dabei wird BlendMode nur auf den Inhalt dieser zusammensetzbaren Funktion angewendet. Das Bild wird über dem bereits auf dem Bildschirm gerenderten Inhalt gerendert, ohne dass dies Auswirkungen auf die bereits gezeichneten Inhalte hat.

Modifier.drawWithContent auf einem Bild mit einem Kreissymbol, mit der Option "BlendMode.Clear" in der App
Abbildung 12: Modifier.drawWithContent auf einem Bild mit einem Kreissymbol mit den Optionen "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-Zwischenspeicher des Fensters (schwarz) bleibt sichtbar. Viele BlendModes mit Alpha funktionieren ohne einen Offscreen-Zwischenspeicher nicht wie erwartet. Achten Sie auf den schwarzen Ring um den roten Kreis:

Modifier.drawWithContent auf einem Bild mit einem Kreissymbol, mit dem Modus BlendMode.Clear und ohne CompositingStrategy
Abbildung 13: „Modifier.drawWithContent“ auf einem Bild mit einem Kreissymbol, wobei „BlendMode.Clear“ und keine „CompositingStrategy“ festgelegt sind

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:

Keine CompositingStrategy festgelegt und BlendMode.Clear wird mit einer App mit einem durchscheinenden Fensterhintergrund verwendet. Der rosa Hintergrund wird durch den Bereich um den roten Statuskreis herum angezeigt.
Abbildung 14: Keine CompositingStrategy festgelegt und Verwendung von BlendMode.Clear bei einer App mit durchscheinendem Fensterhintergrund Beachten Sie, wie der rosa Hintergrund um den roten Statuskreis herum angezeigt wird.

Bei Verwendung von CompositingStrategy.Offscreen wird eine nicht sichtbare Textur erstellt, die der Größe des Zeichenbereichs entspricht, und wieder auf dem Bildschirm gerendert. Alle Zeichenbefehle, die mit dieser Strategie ausgeführt werden, werden standardmäßig auf diesen Bereich begrenzt. Das folgende Code-Snippet veranschaulicht die Unterschiede bei der Verwendung von nicht sichtbaren 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 vs CompositingStrategy.Offscreen - Aus dem Bildschirm wird die Region abgeschnitten, in der dies nicht der Fall ist.
Abbildung 15: CompositingStrategy.Auto vs. CompositingStrategy.Offscreen – Offscreen-Video wird an die Region angepasst, in der dies nicht der Fall ist
ModulateAlpha

Diese Kompositionsstrategie moduliert den Alphawert für jede der in graphicsLayer aufgezeichneten Zeichenanweisungen. 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 unterschiedliche Ergebnisse liefern. 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.

Ein weiteres Beispiel für verschiedene Kompositionsstrategien ist das Anwenden verschiedener Alphas auf verschiedene Teile der zusammensetzbaren Funktionen und das Anwenden einer Modulate-Strategie:

@Preview
@Composable
fun CompositingStratgey_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 das Alpha-Dataset auf jeden einzelnen Zeichenbefehl an.

Inhalte einer zusammensetzbaren Funktion in eine Bitmap schreiben

Ein häufiger Anwendungsfall ist das Erstellen einer Bitmap aus einer zusammensetzbaren Funktion. Wenn Sie den Inhalt der zusammensetzbaren Funktion in eine 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 in den 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 teilen. Weitere Informationen findest du im vollständigen Beispiel-Snippet. Vor dem Speichern auf der Festplatte sollten Sie die Geräteberechtigungen prüfen.

Benutzerdefinierter Zeichnungsmodifikator

Implementieren Sie die DrawModifier-Schnittstelle, um einen eigenen benutzerdefinierten Modifizierer zu erstellen. Dadurch erhalten Sie Zugriff auf ein ContentDrawScope-Objekt, das auch bei Verwendung von Modifier.drawWithContent() verfügbar ist. Anschließend können Sie gängige Zeichenvorgänge in benutzerdefinierte Zeichenmodifikatoren extrahieren, um den Code zu bereinigen und praktische Wrapper bereitzustellen. Beispielsweise ist Modifier.background() eine praktische DrawModifier.

Wenn Sie beispielsweise ein Modifier implementieren möchten, das den Inhalt vertikal umdreht, können Sie es 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 umgedrehten Modifikator auf Text:

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

Benutzerdefinierter umgedrehter Modifizierer für Text
Abbildung 17: Benutzerdefinierter umgedrehter Modifizierer für Text

Zusätzliche Ressourcen

Weitere Beispiele zur Verwendung von graphicsLayer und benutzerdefinierten Zeichnungen finden Sie in den folgenden Ressourcen: