Grafikmodifikatoren

Neben der zusammensetzbaren Funktion Canvas gibt es in der Funktion „Compose“ mehrere nützliche Grafiken Modifiers, die beim Zeichnen benutzerdefinierter Inhalte helfen. Diese Modifikatoren sind nützlich, da sie auf jede zusammensetzbare Funktion angewendet werden können.

Zeichenmodifikatoren

Alle Zeichenbefehle werden in der Funktion „Compose“ mit einem Zeichenmodifikator ausgeführt. In der Funktion „Compose“ gibt es drei Hauptmodifikatoren zum Zeichnen:

Der Basismodifikator für das Zeichnen ist drawWithContent. Damit können Sie die Zeichenreihenfolge der zusammensetzbaren Funktion und die Zeichenbefehle festlegen, die im Modifizierer ausgegeben werden. drawBehind ist ein praktischer Wrapper um drawWithContent, bei dem die Zeichenreihenfolge hinter dem Inhalt der zusammensetzbaren Funktion festgelegt ist. drawWithCache ruft entweder onDrawBehind oder onDrawWithContent darin auf und bietet einen Mechanismus zum Speichern der darin erstellten Objekte im Cache.

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 drawContent auf, um den tatsächlichen Inhalt der zusammensetzbaren Funktion zu rendern. Mit diesem Modifikator können Sie die Reihenfolge der Vorgänge festlegen, wenn Ihr Inhalt vor oder nach Ihren benutzerdefinierten Zeichenvorgängen dargestellt werden soll.

Wenn Sie beispielsweise einen radialen Farbverlauf über Ihren Inhalten rendern möchten, um auf der Benutzeroberfläche einen Taschenlampen-Schlüssellocheffekt 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 auf einer zusammensetzbaren Funktion verwendet, um eine Taschenlampen-Benutzeroberfläche zu erstellen.

Modifier.drawBehind: Zeichnung hinter einer zusammensetzbaren Funktion

Mit Modifier.drawBehind können Sie DrawScope-Vorgänge hinter den zusammensetzbaren Inhalten ausführen, die auf dem Bildschirm dargestellt werden. Wenn Sie sich die Implementierung von Canvas ansehen, werden Sie möglicherweise feststellen, dass es nur ein praktischer Wrapper um Modifier.drawBehind ist.

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)
)

Dies führt zu folgendem Ergebnis:

Text und Hintergrund, der mit Modifier.drawBehind gezeichnet wurde
Abbildung 2: Mit Modifier.drawBehind gezeichneter Text und Hintergrund

Modifier.drawWithCache: Zeichenobjekte zeichnen und im Cache speichern

Modifier.drawWithCache speichert die darin erstellten Objekte im Cache. 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, da keine Objekte (z. B. Brush, Shader, Path) neu zugewiesen werden müssen, die beim Zeichnen erstellt werden.

Alternativ können Sie Objekte auch mit remember außerhalb des Modifizierers im Cache speichern. Das ist jedoch nicht immer möglich, da Sie nicht immer Zugriff auf die Komposition haben. Die Verwendung von drawWithCache ist möglicherweise leistungsfähiger, 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 mithilfe von drawWithCache im Cache gespeichert, bis sich die Größe des Zeichnungsbereichs ändert:

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

Das Brush-Objekt mitdrawWithCache zwischenspeichern
Abbildung 3: Das 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 Funktion in einer Zeichenebene dargestellt wird. Eine Ebene bietet verschiedene Funktionen, z. B.:

  • Isolation der Zeichenanweisungen (ähnlich wie bei RenderNode). Zeichenanweisungen, die als Teil einer Ebene erfasst wurden, können von der Rendering-Pipeline effizient neu ausgegeben werden, ohne Anwendungscode neu ausführen zu müssen.
  • Transformationen, die für alle Zeichenanweisungen in einer Ebene gelten.
  • Rasterung für Kompositionsfunktionen. Bei der Rasterung einer Ebene werden ihre Zeichenanweisungen ausgeführt und die Ausgabe wird in einem nicht sichtbaren Zwischenspeicher erfasst. Das Zusammensetzen 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 isoliert die Zeichenanweisungen. Mit Modifier.graphicsLayer können beispielsweise verschiedene Transformationen 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 Platzierung der zusammensetzbaren Funktion, sondern wirkt sich nur auf die Zeichenphase aus. Das bedeutet, dass Ihre zusammensetzbare Funktion andere überlappen kann, wenn sie am Ende außerhalb der Layoutgrenzen dargestellt wird.

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

Skalierung – Größe erhöhen

Mit scaleX und scaleY wird der Inhalt in horizontale bzw. vertikale Richtung vergrößert bzw. verkleinert. Ein Wert von 1.0f gibt an, dass sich der Maßstab nicht ändert, ein Wert von 0.5f steht für 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: Auf eine zusammensetzbare Image-Anzeige angewendete scaleX und scaleY
Ü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 werden mit Modifier.graphicsLayer auf das Bild angewendet.
Drehung

Legen Sie rotationX für eine horizontale Drehung, rotationY für eine vertikale Anzeigenrotation und rotationZ für eine Rotation auf der Z-Achse fest (Standarddrehung). 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, die von Modifier.graphicsLayer für das Bild festgelegt wurden
Origin

Ein transformOrigin kann angegeben werden. Es wird dann als der Punkt verwendet, von dem aus Transformationen stattfinden. Bisher wurde in allen Beispielen TransformOrigin.Center verwendet, der 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 das Element oben links in der zusammensetzbaren Funktion rotiert:

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, bei der TransformOrigin auf 0f, 0f festgelegt ist

Clip und Form

Die Form gibt den Umriss an, auf den der Inhalt zugeschnitten wird, wenn clip = true. In diesem Beispiel legen wir zwei Felder so fest, dass sie zwei verschiedene Clips haben: einen mit der Clip-Variable graphicsLayer und der 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 an die Kreisform gekürzt:

Clip wurde auf zusammensetzbare Funktion von Box angewendet
Abbildung 8: Clip auf zusammensetzbare Funktion von Box angewendet

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

Clip mit ÜbersetzungY und rotem Rahmen für Umriss angewendet
Abbildung 9: Clip angewendet mit „TranslationY“ und rotem Rahmen für den Umriss

Wenn Sie die zusammensetzbare Funktion auf den Bereich zuschneiden möchten, in dem sie gezeichnet ist, 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 überlagert GrafikLayer-Transformation angewendet
Abbildung 10: Clip überlagert Grafikebene (GraphicsLayer-Transformation)

Alpha

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

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

Bild mit angewendetem Alpha
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 dem Ändern einer Alphaversion können Sie auch einen CompositingStrategy für eine graphicsLayer festlegen. Ein CompositingStrategy bestimmt, wie der Inhalt der zusammensetzbaren Funktion mit den anderen Inhalten, die bereits auf dem Bildschirm dargestellt sind, zusammengesetzt wird.

Folgende Strategien stehen zur Verfügung:

Automatisch (Standard)

Die Zusammensetzungsstrategie wird durch die restlichen graphicsLayer-Parameter bestimmt. Die Ebene wird in einen nicht sichtbaren Zwischenspeicher gerendert, wenn der Alphawert kleiner als 1,0f ist oder ein RenderEffect festgelegt ist. Immer wenn der Alphawert kleiner als 1f ist, wird automatisch eine Erstellungsebene erstellt, um die Inhalte zu rendern und dann diesen nicht sichtbaren Zwischenspeicher mit dem entsprechenden Alphatest an das Ziel zu übertragen. Wenn Sie RenderEffect oder Overscroll festlegen, werden Inhalte unabhängig vom festgelegten CompositingStrategy immer in einem nicht sichtbaren Zwischenspeicher gerendert.

Nicht sichtbar

Der Inhalt der zusammensetzbaren Funktion wird vor dem Rendern für das Ziel immer auf eine nicht sichtbare Textur oder Bitmap gerastert. Dies ist nützlich, um BlendMode-Vorgänge zum Maskieren von Inhalten anzuwenden und um die Leistung beim Rendern komplexer Sätze von Zeichenanweisungen zu verbessern.

Ein Beispiel für die Verwendung von CompositingStrategy.Offscreen ist BlendModes. Im folgenden Beispiel möchten Sie Teile einer zusammensetzbaren Funktion Image entfernen, indem Sie einen Zeichenbefehl ausführen, der BlendMode.Clear verwendet. Wenn Sie compositingStrategy nicht auf CompositingStrategy.Offscreen setzen, interagiert BlendMode mit seinem gesamten Inhalt.

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 können. Dabei wird BlendMode nur auf den Inhalt dieser zusammensetzbaren Funktion angewendet. Diese wird dann über dem bereits auf dem Bildschirm gerenderten Inhalt gerendert, ohne dass sich dies auf die bereits gezeichneten Inhalte auswirkt.

Modifier.drawWithContent auf einem Bild mit einer Kreisanzeige mit der Funktion "BlendMode.Clear" in der App
Abbildung 12: Modifier.drawWithContent auf einem Bild mit kreisförmiger Anzeige und 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 Renderingzwischenspeicher des Fensters (Schwarz) bleibt sichtbar. Viele der BlendModes mit Alpha funktionieren ohne einen Offscreen-Zwischenspeicher nicht wie erwartet. Beachten Sie den schwarzen Ring um den roten Kreisindikator:

Modifier.drawWithContent für ein Bild mit einer kreisförmigen Anzeige, wobei BlendMode.Clear und keine CompositingStrategy festgelegt sind
Abbildung 13: Modifier.drawWithContent auf einem Bild mit kreisförmiger Anzeige, wobei BlendMode.Clear und keine CompositingStrategy festgelegt sind

Zum besseren Verständnis: Wenn die App einen durchscheinenden Fensterhintergrund hätte und Sie CompositingStrategy.Offscreen nicht verwenden würden, würde BlendMode mit der gesamten App interagieren. Alle Pixel würden alle Pixel löschen, um die App oder den Hintergrund darunter anzuzeigen, wie in diesem Beispiel:

Keine CompositingStrategy festgelegt und BlendMode.Clear mit einer App mit durchsichtigem Fensterhintergrund verwendet. Der rosafarbene Hintergrund wird durch den Bereich um den roten Statuskreis herum angezeigt.
Abbildung 14: Keine CompositingStrategy festgelegt und BlendMode.Clear mit einer App mit durchsichtigem Fensterhintergrund verwendet. Beachten Sie, wie der rosafarbene Hintergrund durch den Bereich um den roten Statuskreis herum angezeigt wird.

Wenn Sie CompositingStrategy.Offscreen verwenden, wird eine nicht sichtbare Textur in der Größe des Zeichenbereichs erstellt und wieder auf dem Bildschirm gerendert. Alle Zeichenbefehle, die mit dieser Strategie ausgeführt werden, werden standardmäßig auf diese Region beschränkt. Das folgende Code-Snippet zeigt die Unterschiede beim Wechsel zu 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 – Clips werden außerhalb des sichtbaren Bereichs der Region abgespielt, in der dies nicht der Fall ist.
Abbildung 15: CompositingStrategy.Auto vs. CompositingStrategy.Offscreen – Clips außerhalb des sichtbaren Bereichs der Region, in der dies nicht der Fall ist
ModulateAlpha

Diese Zusammensetzungsstrategie moduliert den Alphawert für jede der Zeichenanweisungen, die in graphicsLayer aufgezeichnet wurden. Für Alpha-Werte unter 1,0f wird kein Offline-Zwischenspeicher erstellt, es sei denn, RenderEffect ist festgelegt, sodass das Alpha-Rendering effizienter sein kann. Sie können jedoch zu unterschiedlichen Ergebnissen bei sich überschneidenden Inhalten führen. Für Anwendungsfälle, in denen im Voraus bekannt ist, dass sich die Inhalte nicht überschneiden, kann dies eine bessere Leistung als CompositingStrategy.Auto mit Alphawerten unter 1 bieten.

Weiter unten finden Sie ein weiteres Beispiel für verschiedene Zusammensetzungsstrategien. Hier werden verschiedene Alphas auf verschiedene Teile der zusammensetzbaren Funktionen angewendet und es wird eine Modulate-Strategie angewendet:

@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 Alphasatz auf jeden einzelnen Zeichenbefehl an.
Abbildung 16: ModulateAlpha wendet den Alphasatz auf jeden einzelnen Zeichenbefehl an

Inhalt einer zusammensetzbaren Funktion in eine Bitmap schreiben

Ein häufiger Anwendungsfall besteht darin, eine Bitmap aus einer zusammensetzbaren Funktion zu erstellen. Wenn Sie den Inhalt Ihrer zusammensetzbaren Funktion 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 im 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 teilen. Weitere Informationen finden Sie im vollständigen Beispiel-Snippet. Überprüfen Sie die Geräteberechtigungen, bevor Sie Dateien speichern.

Benutzerdefinierter Modifikator für Zeichnung

Implementieren Sie die DrawModifier-Schnittstelle, um einen eigenen benutzerdefinierten Modifikator zu erstellen. Dadurch erhalten Sie Zugriff auf ein ContentDrawScope-Objekt, das mit dem Wert übereinstimmt, der bei Verwendung von Modifier.drawWithContent() bereitgestellt wird. Anschließend können Sie gängige 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 ein Modifier implementieren möchten, das Inhalte vertikal dreht, können Sie Folgendes erstellen:

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

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

Verwende dann diesen umgedrehten Modifikator, der auf Text angewendet wird:

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

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

Weitere Informationen

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