Viele Apps müssen genau steuern können, was auf dem Bildschirm angezeigt wird. Das kann so einfach sein wie das Platzieren eines Rechtecks oder Kreises an der richtigen Stelle auf dem Bildschirm oder so komplex wie eine aufwendige Anordnung von Grafikelementen in vielen verschiedenen Stilen.
Einfache Zeichnung mit Modifikatoren und DrawScope
Die wichtigste Methode, um in Compose etwas Benutzerdefiniertes zu zeichnen, sind Modifikatoren wie Modifier.drawWithContent, Modifier.drawBehind und Modifier.drawWithCache.
Wenn Sie beispielsweise etwas hinter Ihrer zusammensetzbaren Funktion zeichnen möchten, können Sie den Modifikator drawBehind verwenden, um Zeichenbefehle auszuführen:
Spacer( modifier = Modifier .fillMaxSize() .drawBehind { // this = DrawScope } )
Wenn Sie nur eine zusammensetzbare Funktion benötigen, die zeichnet, können Sie die zusammensetzbare Funktion Canvas verwenden. Das Compose-Element Canvas ist ein praktischer Wrapper für Modifier.drawBehind. Sie platzieren die Canvas in Ihrem Layout genauso wie jedes andere Compose-UI-Element. In der Canvas können Sie Elemente zeichnen und deren Stil und Position genau steuern.
Alle Zeichenmodifikatoren stellen einen DrawScope bereit, eine Zeichenumgebung mit eigenem Bereich, die ihren eigenen Status beibehält. Damit können Sie die Parameter für eine Gruppe grafischer Elemente festlegen. DrawScope bietet mehrere nützliche Felder, z. B. size, ein Size-Objekt, das die aktuellen Abmessungen von DrawScope angibt.
Wenn Sie etwas zeichnen möchten, können Sie eine der vielen Zeichenfunktionen auf DrawScope verwenden. Mit dem folgenden Code wird beispielsweise ein Rechteck in der oberen linken Ecke des Bildschirms gezeichnet:
Canvas(modifier = Modifier.fillMaxSize()) { val canvasQuadrantSize = size / 2F drawRect( color = Color.Magenta, size = canvasQuadrantSize ) }
Weitere Informationen zu den verschiedenen Zeichenmodifikatoren finden Sie in der Dokumentation zu Grafikmodifikatoren.
Koordinatensystem
Wenn Sie etwas auf dem Bildschirm zeichnen möchten, müssen Sie den Versatz (x und y) und die Größe des Elements kennen. Bei vielen der Zeichenmethoden für DrawScope werden Position und Größe durch Standardparameterwerte angegeben. Die Standardparameter positionieren das Element im Allgemeinen am Punkt [0, 0] im Canvas und stellen einen Standard-size bereit, der den gesamten Zeichenbereich ausfüllt, wie im Beispiel oben. Sie können sehen, dass das Rechteck oben links positioniert ist. Wenn Sie die Größe und Position Ihres Elements anpassen möchten, müssen Sie das Koordinatensystem in Compose kennen.
Der Ursprung des Koordinatensystems ([0,0]) befindet sich am Pixel ganz oben links im Zeichenbereich. x nimmt nach rechts zu und y nach unten.
Wenn Sie beispielsweise eine diagonale Linie von der oberen rechten Ecke des Canvas-Bereichs zur unteren linken Ecke zeichnen möchten, können Sie die Funktion DrawScope.drawLine() verwenden und einen Start- und End-Offset mit den entsprechenden x- und y-Positionen angeben:
Canvas(modifier = Modifier.fillMaxSize()) { val canvasWidth = size.width val canvasHeight = size.height drawLine( start = Offset(x = canvasWidth, y = 0f), end = Offset(x = 0f, y = canvasHeight), color = Color.Blue ) }
Grundlegende Transformationen
DrawScope bietet Transformationen, mit denen Sie ändern können, wo oder wie die Zeichenbefehle ausgeführt werden.
Skalieren
Mit DrawScope.scale() können Sie die Größe Ihrer Zeichenvorgänge um einen Faktor erhöhen. Vorgänge wie scale() gelten für alle Zeichenvorgänge innerhalb des entsprechenden Lambda-Ausdrucks. Mit dem folgenden Code wird beispielsweise scaleX um das Zehnfache und scaleY um das 15-Fache erhöht:
Canvas(modifier = Modifier.fillMaxSize()) { scale(scaleX = 10f, scaleY = 15f) { drawCircle(Color.Blue, radius = 20.dp.toPx()) } }
Übersetzen
Verwenden Sie
DrawScope.translate(), um die Zeichenvorgänge nach oben, unten, links oder rechts zu verschieben. Mit dem folgenden Code wird die Zeichnung beispielsweise um 100 px nach rechts und um 300 px nach oben verschoben:
Canvas(modifier = Modifier.fillMaxSize()) { translate(left = 100f, top = -300f) { drawCircle(Color.Blue, radius = 200.dp.toPx()) } }
Drehen
Mit DrawScope.rotate() können Sie Ihre Zeichenvorgänge um einen Drehpunkt drehen. Mit dem folgenden Code wird beispielsweise ein Rechteck um 45 Grad gedreht:
Canvas(modifier = Modifier.fillMaxSize()) { rotate(degrees = 45F) { drawRect( color = Color.Gray, topLeft = Offset(x = size.width / 3F, y = size.height / 3F), size = size / 3F ) } }
rotate() wird eine Drehung auf den aktuellen Zeichenbereich angewendet, wodurch das Rechteck um 45 Grad gedreht wird.
Abgesenkt
Verwenden Sie DrawScope.inset(), um die Standardparameter des aktuellen DrawScope anzupassen, die Zeichnungsgrenzen zu ändern und die Zeichnungen entsprechend zu übersetzen:
Canvas(modifier = Modifier.fillMaxSize()) { val canvasQuadrantSize = size / 2F inset(horizontal = 50f, vertical = 30f) { drawRect(color = Color.Green, size = canvasQuadrantSize) } }
Mit diesem Code wird den Zeichenbefehlen ein Innenrand hinzugefügt:
Mehrere Transformationen
Wenn Sie mehrere Transformationen auf Ihre Zeichnungen anwenden möchten, verwenden Sie die Funktion DrawScope.withTransform(). Damit wird eine einzelne Transformation erstellt und angewendet, die alle gewünschten Änderungen kombiniert. Die Verwendung von withTransform() ist effizienter als verschachtelte Aufrufe einzelner Transformationen, da alle Transformationen zusammen in einem einzigen Vorgang ausgeführt werden, anstatt dass Compose jede der verschachtelten Transformationen berechnen und speichern muss.
Mit dem folgenden Code wird beispielsweise sowohl eine Verschiebung als auch eine Drehung auf das Rechteck angewendet:
Canvas(modifier = Modifier.fillMaxSize()) { withTransform({ translate(left = size.width / 5F) rotate(degrees = 45F) }) { drawRect( color = Color.Gray, topLeft = Offset(x = size.width / 3F, y = size.height / 3F), size = size / 3F ) } }
withTransform, um sowohl eine Drehung als auch eine Verschiebung anzuwenden. Dadurch wird das Rechteck gedreht und nach links verschoben.Häufige Zeichenvorgänge
Text zeichnen
Wenn Sie Text in Compose zeichnen möchten, können Sie normalerweise die zusammensetzbare Funktion Text verwenden. Wenn Sie sich jedoch in einem DrawScope befinden oder Ihren Text manuell mit Anpassungen zeichnen möchten, können Sie die Methode DrawScope.drawText() verwenden.
Wenn Sie Text zeichnen möchten, erstellen Sie mit rememberTextMeasurer einen TextMeasurer und rufen Sie drawText mit dem Measurer auf:
val textMeasurer = rememberTextMeasurer() Canvas(modifier = Modifier.fillMaxSize()) { drawText(textMeasurer, "Hello") }
Text messen
Das Zeichnen von Text funktioniert etwas anders als andere Zeichenbefehle. Normalerweise geben Sie dem Zeichenbefehl die Größe (Breite und Höhe) an, in der die Form oder das Bild gezeichnet werden soll. Bei Text gibt es einige Parameter, die die Größe des gerenderten Texts steuern, z. B. Schriftgröße, Schriftart, Ligaturen und Zeichenabstand.
In Compose können Sie mit TextMeasurer auf die gemessene Größe des Textes zugreifen, abhängig von den oben genannten Faktoren. Wenn Sie einen Hintergrund hinter dem Text zeichnen möchten, können Sie die gemessenen Informationen verwenden, um die Größe des Bereichs zu ermitteln, den der Text einnimmt:
val textMeasurer = rememberTextMeasurer() Spacer( modifier = Modifier .drawWithCache { val measuredText = textMeasurer.measure( AnnotatedString(longTextSample), constraints = Constraints.fixedWidth((size.width * 2f / 3f).toInt()), style = TextStyle(fontSize = 18.sp) ) onDrawBehind { drawRect(pinkColor, size = measuredText.size.toSize()) drawText(measuredText) } } .fillMaxSize() )
Dieses Code-Snippet erzeugt einen pinken Hintergrund für den Text:
Wenn Sie die Einschränkungen, die Schriftgröße oder ein anderes Attribut anpassen, das sich auf die gemessene Größe auswirkt, wird eine neue Größe gemeldet. Sie können sowohl für width als auch für height eine feste Größe festlegen. Der Text folgt dann der festgelegten TextOverflow. Mit dem folgenden Code wird beispielsweise Text in einem Drittel der Höhe und einem Drittel der Breite des zusammensetzbaren Bereichs gerendert und TextOverflow auf TextOverflow.Ellipsis festgelegt:
val textMeasurer = rememberTextMeasurer() Spacer( modifier = Modifier .drawWithCache { val measuredText = textMeasurer.measure( AnnotatedString(longTextSample), constraints = Constraints.fixed( width = (size.width / 3f).toInt(), height = (size.height / 3f).toInt() ), overflow = TextOverflow.Ellipsis, style = TextStyle(fontSize = 18.sp) ) onDrawBehind { drawRect(pinkColor, size = measuredText.size.toSize()) drawText(measuredText) } } .fillMaxSize() )
Der Text wird jetzt innerhalb der Einschränkungen mit Auslassungspunkten am Ende dargestellt:
TextOverflow.Ellipsis mit festen Einschränkungen für die Messung von Text.Bild zeichnen
Wenn Sie mit DrawScope ein ImageBitmap zeichnen möchten, laden Sie das Bild mit ImageBitmap.imageResource() und rufen Sie dann drawImage auf:
val dogImage = ImageBitmap.imageResource(id = R.drawable.dog) Canvas(modifier = Modifier.fillMaxSize(), onDraw = { drawImage(dogImage) })
ImageBitmap auf Canvas.Grundformen zeichnen
In DrawScope gibt es viele Funktionen zum Zeichnen von Formen. Verwenden Sie zum Zeichnen einer Form eine der vordefinierten Zeichenfunktionen, z. B. drawCircle:
val purpleColor = Color(0xFFBA68C8) Canvas( modifier = Modifier .fillMaxSize() .padding(16.dp), onDraw = { drawCircle(purpleColor) } )
API |
Ausgabe |
|
|
|
|
|
|
|
|
|
|
|
|
|
Pfad zeichnen
Ein Pfad ist eine Reihe mathematischer Anweisungen, die nach der Ausführung zu einer Zeichnung führen. DrawScope kann mit der Methode DrawScope.drawPath() einen Pfad zeichnen.
Angenommen, Sie möchten ein Dreieck zeichnen. Sie können einen Pfad mit Funktionen wie lineTo() und moveTo() generieren, indem Sie die Größe des Zeichenbereichs verwenden.
Rufen Sie dann drawPath() mit diesem neu erstellten Pfad auf, um ein Dreieck zu erhalten.
Spacer( modifier = Modifier .drawWithCache { val path = Path() path.moveTo(0f, 0f) path.lineTo(size.width / 2f, size.height / 2f) path.lineTo(size.width, 0f) path.close() onDrawBehind { drawPath(path, Color.Magenta, style = Stroke(width = 10f)) } } .fillMaxSize() )
Path in Compose erstellen und zeichnen.Auf Canvas-Objekt zugreifen
Bei DrawScope haben Sie keinen direkten Zugriff auf ein Canvas-Objekt. Mit DrawScope.drawIntoCanvas() können Sie auf das Canvas-Objekt selbst zugreifen, für das Sie Funktionen aufrufen können.
Wenn Sie beispielsweise ein benutzerdefiniertes Drawable auf dem Canvas zeichnen möchten, können Sie auf den Canvas zugreifen und Drawable#draw() aufrufen und das Canvas-Objekt übergeben:
val drawable = ShapeDrawable(OvalShape()) Spacer( modifier = Modifier .drawWithContent { drawIntoCanvas { canvas -> drawable.setBounds(0, 0, size.width.toInt(), size.height.toInt()) drawable.draw(canvas.nativeCanvas) } } .fillMaxSize() )
Drawable zu zeichnen.Weitere Informationen
Weitere Informationen zum Zeichnen in Compose finden Sie in den folgenden Ressourcen:
- Grafikmodifikatoren: Hier finden Sie Informationen zu den verschiedenen Arten von Zeichenmodifikatoren.
- Pinsel: Hier erfährst du, wie du das Zeichnen deiner Inhalte anpassen kannst.
- Benutzerdefinierte Layouts und Grafiken in Compose – Android Dev Summit 2022: Hier erfährst du, wie du mit Layouts und Grafiken eine benutzerdefinierte UI in Compose erstellst.
- JetLagged-Beispiel: Compose-Beispiel, das zeigt, wie ein benutzerdefiniertes Diagramm gezeichnet wird.
Empfehlungen für Sie
- Hinweis: Der Linktext wird angezeigt, wenn JavaScript deaktiviert ist.
- Grafikmodifikatoren
- Grafiken in Compose
- Ausrichtungslinien in Jetpack Compose (Englisch)