Grafiken in Compose

Viele Apps müssen in der Lage sein, genau zu steuern, was auf den Karten Bildschirm. Sie könnten zum Beispiel einen Kasten oder einen Kreis auf dem Bildschirm platzieren. oder eine aufwendige Anordnung grafischer Elemente in vielen verschiedenen Stilen.

Einfache Zeichnung mit Modifikatoren und DrawScope

Benutzerdefinierte Elemente lassen sich in „Schreiben“ hauptsächlich mit Modifikatoren zeichnen, z. B. Modifier.drawWithContent, Modifier.drawBehind und Modifier.drawWithCache

Um beispielsweise etwas hinter der zusammensetzbaren Funktion zu zeichnen, können Sie die Funktion drawBehind-Modifikator, um mit der Ausführung von Zeichenbefehlen zu beginnen:

Spacer(
    modifier = Modifier
        .fillMaxSize()
        .drawBehind {
            // this = DrawScope
        }
)

Wenn Sie nur eine zusammensetzbare Funktion benötigen, Canvas zusammensetzbar. Die zusammensetzbare Funktion Canvas ist ein praktischer Wrapper um Modifier.drawBehind. Du platzierst Canvas so wie bei jedem anderen UI-Element von „Compose“. Im Canvas können Sie Elemente zeichnen und dabei ihren Stil und Standort.

Alle Zeichenmodifikatoren bieten DrawScope, eine auf einen Bereich reduzierte Zeichenumgebung. das seinen eigenen Zustand beibehält. So können Sie die Parameter für eine Gruppe grafische Elemente. Das DrawScope bietet mehrere nützliche Felder, z. B. size, Ein Size-Objekt, das die aktuellen Abmessungen von DrawScope angibt.

Um etwas zu zeichnen, können Sie eine der vielen Zeichenfunktionen in DrawScope verwenden. Für Beispiel: Mit dem folgenden Code wird ein Rechteck in der oberen linken Ecke des Bildschirm:

Canvas(modifier = Modifier.fillMaxSize()) {
    val canvasQuadrantSize = size / 2F
    drawRect(
        color = Color.Magenta,
        size = canvasQuadrantSize
    )
}

<ph type="x-smartling-placeholder">
</ph> Rosa Rechteck, gezeichnet auf einem weißen Hintergrund, der ein Viertel des Bildschirms einnimmt
Abbildung 1: Rechteck, das mit Canvas in „Compose“ gezeichnet wurde

Weitere Informationen zu verschiedenen Modifikatoren für Zeichnungen finden Sie unter Grafikmodifikatoren. Dokumentation.

Koordinatensystem

Um etwas auf dem Bildschirm zu zeichnen, müssen Sie den Versatz (x und y) und die Größe des Ihren Artikel. Bei vielen der Zeichenmethoden auf DrawScope sind die Position und Größe werden durch Standardparameterwerte bereitgestellt. Die Standardparameter sind in der Regel Element am Punkt [0, 0] auf dem Canvas positionieren und einen Standardwert für size, das wie im Beispiel oben den gesamten Zeichenbereich ausfüllt. wird das Rechteck oben links positioniert. Um die Größe und Position der müssen Sie mit dem Koordinatensystem in Compose vertraut sein.

Der Ursprung des Koordinatensystems ([0,0]) befindet sich am Pixel ganz oben links in der Zeichnungsbereich. Der Wert von x steigt bei Bewegung nach rechts und y im Laufe der Zeit an. nach unten zeigen.

<ph type="x-smartling-placeholder">
</ph> Ein Raster mit dem Koordinatensystem oben links [0, 0] und unten rechts [Breite, Höhe].
Abbildung 2: Koordinatensystem zeichnen / Raster zeichnen.

Wenn Sie zum Beispiel eine diagonale Linie von der oberen rechten Ecke Canvas-Bereich unten links, können Sie den DrawScope.drawLine()-Funktion und geben Sie einen Start- und Endversatz mit entsprechenden x- und y-Positionen:

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 sich ändern lässt, wo oder wie die Zeichenbefehle verwendet werden ausgeführt werden.

Skalieren

Verwenden Sie DrawScope.scale() um die Größe Ihrer Zeichenvorgänge um einen Faktor zu erhöhen. Vorgänge wie scale() gelten für alle Zeichenvorgänge innerhalb der entsprechenden Lambda-Funktion. Für Beispiel: Durch den folgenden Code werden scaleX um das 10-Fache und scaleY um 15 erhöht. Mal:

Canvas(modifier = Modifier.fillMaxSize()) {
    scale(scaleX = 10f, scaleY = 15f) {
        drawCircle(Color.Blue, radius = 20.dp.toPx())
    }
}

<ph type="x-smartling-placeholder">
</ph> Ein ungleichförmig skalierter Kreis
Abbildung 3: Skalierungsvorgang auf einen Kreis auf Canvas anwenden

Übersetzen

Verwenden Sie DrawScope.translate() um Ihre Zeichenoperationen nach oben, unten, links oder rechts zu verschieben. Beispiel: Der Parameter Mit dem folgenden Code wird die Zeichnung um 100 Pixel nach rechts und 300 Pixel nach oben verschoben:

Canvas(modifier = Modifier.fillMaxSize()) {
    translate(left = 100f, top = -300f) {
        drawCircle(Color.Blue, radius = 200.dp.toPx())
    }
}

<ph type="x-smartling-placeholder">
</ph> Ein Kreis, der aus der Mitte verschoben wurde
Abbildung 4: Übersetzungsvorgang auf einen Kreis in Canvas anwenden

Drehen

Verwenden Sie DrawScope.rotate() um Ihre Zeichenvorgänge um einen Drehpunkt zu drehen. Beispiel: Der Parameter Mit dem folgenden Code wird 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
        )
    }
}

<ph type="x-smartling-placeholder">
</ph> Ein Smartphone mit einem um 45 Grad gedrehten Rechteck in der Mitte des Displays
Abbildung 5. Wir verwenden rotate(), um eine Drehung auf den aktuellen Zeichenbereich anzuwenden, wodurch das Rechteck um 45 Grad gedreht wird.

Einsatz

Mit DrawScope.inset() können Sie die Standardparameter der aktuellen DrawScope, wobei die Grenzen der Zeichnung geändert und die Zeichnungen übersetzt werden entsprechend:

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 effektiv ein Padding hinzugefügt:

<ph type="x-smartling-placeholder">
</ph> Ein Rechteck mit rundem Abstand.
Abbildung 6: Ein Einfügen auf Zeichenbefehle anwenden.

Mehrere Transformationen

Um mehrere Transformationen auf Ihre Zeichnungen anzuwenden, verwenden Sie die Methode DrawScope.withTransform()-Funktion, die ein wendet eine einzelne Transformation an, in der alle gewünschten Änderungen kombiniert werden. Mit withTransform() ist effizienter als verschachtelte Aufrufe an einzelne Nutzer. da alle Transformationen gemeinsam in einem anstatt Compose einzeln zu berechnen und zu speichern, verschachtelte Transformationen ein.

Mit dem folgenden Code werden beispielsweise sowohl eine Übersetzung als auch eine Rotation auf den Rechteck:

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

<ph type="x-smartling-placeholder">
</ph> Ein Smartphone mit einem gedrehten Rechteck, das auf die Seite des Displays verschoben wurde
Abbildung 7. Mit withTransform können Sie sowohl eine Drehung als auch eine Verschiebung anwenden. Drehen Sie dazu das Rechteck und verschieben Sie es nach links.

Gängige Zeichenvorgänge

Text zeichnen

Um Text in „Schreiben“ zu zeichnen, können Sie normalerweise die zusammensetzbare Funktion Text verwenden. Sie können jedoch Wenn Sie sich in einem DrawScope befinden oder Ihren Text manuell mit können Sie die Methode DrawScope.drawText() .

Wenn Sie Text zeichnen möchten, erstellen Sie mit rememberTextMeasurer ein TextMeasurer und rufen Sie drawText mit dem Measurer auf:

val textMeasurer = rememberTextMeasurer()

Canvas(modifier = Modifier.fillMaxSize()) {
    drawText(textMeasurer, "Hello")
}

<ph type="x-smartling-placeholder">
</ph> Auf Canvas gezeichnete Hallo
Abbildung 8. Text auf Canvas zeichnen.

Text messen

Das Zeichnen von Text unterscheidet sich etwas von anderen Zeichenbefehlen. Normalerweise Geben Sie im Zeichenbefehl die Größe (Breite und Höhe) an, um die Form bzw. das Bild zu zeichnen. Bei Text gibt es einige Parameter, die die Größe des gerenderten Text, wie etwa Schriftgröße, Schriftart, Ligaturen und Buchstabenabstand.

Mit der Funktion „Schreiben“ erhalten Sie mit einem TextMeasurer Zugriff auf die gemessenen Textgröße abhängig von den oben genannten Faktoren. Wenn Sie einen Hintergrund zeichnen möchten hinter dem Text können Sie anhand der gemessenen Informationen die Größe Bereich, 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()
)

Bei diesem Code-Snippet wird der Text mit einem rosa Hintergrund versehen:

<ph type="x-smartling-placeholder">
</ph> Mehrzeiliger Text, der 2⁄3 der gesamten Fläche einnimmt, mit einem Hintergrundrechteck
Abbildung 9. Mehrzeiliger Text, der 2⁄3 der gesamten Fläche einnimmt, mit einem Rechteck im Hintergrund.

Anpassen der Einschränkungen, der Schriftgröße oder anderer Eigenschaften, die sich auf die gemessene Größe auswirken führt zu einer neuen Größe. Sie können sowohl für width als auch für und height. Der Text folgt dann auf TextOverflow. Für Beispiel: Der folgende Code rendert Text in 1⁄3 der Höhe und 1⁄3 der Breite des zusammensetzbaren Bereichs und legt TextOverflow auf TextOverflow.Ellipsis fest:

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 in den Einschränkungen mit Auslassungspunkten am Ende gezeichnet:

<ph type="x-smartling-placeholder">
</ph> Auf rosafarbenem Hintergrund gezeichneter Text mit Auslassungspunkten, die den Text abgeschnitten haben.
Abbildung 10: TextOverflow.Ellipsis mit festen Einschränkungen bei der Textmessung.

Bild zeichnen

Um ein ImageBitmap mit DrawScope zu zeichnen, laden Sie das Bild mithilfe von ImageBitmap.imageResource() und dann drawImage aufrufen:

val dogImage = ImageBitmap.imageResource(id = R.drawable.dog)

Canvas(modifier = Modifier.fillMaxSize(), onDraw = {
    drawImage(dogImage)
})

<ph type="x-smartling-placeholder">
</ph> Ein auf Canvas gezeichnetes Bild eines Hundes
Abbildung 11. ImageBitmap auf Canvas zeichnen

Grundformen zeichnen

In DrawScope gibt es viele Funktionen zum Zeichnen von Formen. Um eine Form zu zeichnen, der vordefinierten Zeichenfunktionen wie drawCircle:

val purpleColor = Color(0xFFBA68C8)
Canvas(
    modifier = Modifier
        .fillMaxSize()
        .padding(16.dp),
    onDraw = {
        drawCircle(purpleColor)
    }
)

API

Ausgabe

drawCircle()

Kreis zeichnen

drawRect()

Rechteck zeichnen

drawRoundedRect()

Abgerundetes Rechteck zeichnen

drawLine()

Linie zeichnen

drawOval()

Oval zeichnen

drawArc()

Bogen zeichnen

drawPoints()

Ziehpunkte

Pfad zeichnen

Ein Pfad ist eine Reihe mathematischer Anweisungen, die einmal zu einer Zeichnung führen ausgeführt haben. 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() unter Verwendung der 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()
)

<ph type="x-smartling-placeholder">
</ph> Ein auf dem Kopf stehendes lilafarbenes Dreieck mit dem Pfad „Schreiben“
Abbildung 12. Path in Compose wird erstellt und gezeichnet.

Auf Canvas-Objekt zugreifen

Mit DrawScope haben Sie keinen direkten Zugriff auf ein Canvas-Objekt. Sie können DrawScope.drawIntoCanvas(), um auf das Canvas-Objekt selbst, in dem Sie Funktionen aufrufen können.

Wenn Sie beispielsweise ein benutzerdefiniertes Drawable-Element haben, das Sie auf dem können Sie auf das Canvas zugreifen, Drawable#draw() aufrufen und die Canvas-Objekt:

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

<ph type="x-smartling-placeholder">
</ph> Ein ovales schwarzes ShapeDrawable, das die gesamte Größe einnimmt
Abbildung 13. Zugriff auf den Canvas, um ein Drawable zu zeichnen.

Weitere Informationen

Weitere Informationen zu Zeichnungen in „Compose“ finden Sie in den folgenden Ressourcen: