Molte app devono essere in grado di controllare con precisione ciò che viene disegnato sullo schermo. Potrebbe trattarsi di un'operazione semplice come l'inserimento di un riquadro o di un cerchio sullo schermo nella posizione corretta o di una disposizione elaborata di elementi grafici in molti stili diversi.
Disegno di base con modificatori e DrawScope
Il modo principale per disegnare un elemento personalizzato in Compose è utilizzare modificatori come
Modifier.drawWithContent,
Modifier.drawBehind e
Modifier.drawWithCache.
Ad esempio, per disegnare qualcosa dietro il composable, puoi utilizzare il modificatore drawBehind per iniziare a eseguire i comandi di disegno:
Spacer( modifier = Modifier .fillMaxSize() .drawBehind { // this = DrawScope } )
Se hai bisogno solo di un componibile che disegni, puoi utilizzare il componibile Canvas. Il componibile Canvas è un comodo wrapper per Modifier.drawBehind. Puoi posizionare Canvas nel layout nello stesso modo in cui faresti con qualsiasi altro elemento UI di Compose. All'interno del
Canvas, puoi disegnare elementi con un controllo preciso sul loro stile e sulla loro
posizione.
Tutti i modificatori di disegno espongono un DrawScope, un ambiente di disegno con ambito che mantiene il proprio stato. In questo modo puoi impostare i parametri per un gruppo di elementi grafici. DrawScope fornisce diversi campi utili, come size, un oggetto Size che specifica le dimensioni correnti di DrawScope.
Per disegnare qualcosa, puoi utilizzare una delle tante funzioni di disegno di DrawScope. Ad esempio, il seguente codice disegna un rettangolo nell'angolo in alto a sinistra dello schermo:
Canvas(modifier = Modifier.fillMaxSize()) { val canvasQuadrantSize = size / 2F drawRect( color = Color.Magenta, size = canvasQuadrantSize ) }
Per scoprire di più sui diversi modificatori di disegno, consulta la documentazione relativa ai modificatori di grafica.
Sistema di coordinate
Per disegnare qualcosa sullo schermo, devi conoscere l'offset (x e y) e le dimensioni del tuo elemento. Con molti dei metodi di disegno su DrawScope, la posizione e le dimensioni
vengono fornite dai valori dei parametri predefiniti. In genere, i parametri predefiniti posizionano l'elemento nel punto [0, 0] del canvas e forniscono un size predefinito che riempie l'intera area di disegno, come nell'esempio precedente, dove puoi vedere che il rettangolo è posizionato in alto a sinistra. Per regolare le dimensioni e la posizione dell'elemento, devi comprendere il sistema di coordinate in Compose.
L'origine del sistema di coordinate ([0,0]) si trova nel pixel più in alto a sinistra nell'area di disegno. x aumenta man mano che si sposta verso destra e y aumenta man mano che si sposta verso il basso.
Ad esempio, se vuoi tracciare una linea diagonale dall'angolo in alto a destra dell'area del canvas all'angolo in basso a sinistra, puoi utilizzare la funzione DrawScope.drawLine() e specificare un offset di inizio e di fine con le posizioni x e y corrispondenti:
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 ) }
Trasformazioni di base
DrawScope offre trasformazioni per modificare dove o come vengono eseguiti i comandi di disegno.
Scala
Utilizza
DrawScope.scale()
per aumentare le dimensioni delle operazioni di disegno di un fattore. Le operazioni come scale() si applicano a tutte le operazioni di disegno all'interno della funzione lambda corrispondente. Ad esempio, il seguente codice aumenta scaleX di 10 volte e scaleY di 15 volte:
Canvas(modifier = Modifier.fillMaxSize()) { scale(scaleX = 10f, scaleY = 15f) { drawCircle(Color.Blue, radius = 20.dp.toPx()) } }
Traduci
Utilizza
DrawScope.translate()
per spostare le operazioni di disegno verso l'alto, verso il basso, verso sinistra o verso destra. Ad esempio, il seguente codice sposta il disegno di 100 px a destra e di 300 px in alto:
Canvas(modifier = Modifier.fillMaxSize()) { translate(left = 100f, top = -300f) { drawCircle(Color.Blue, radius = 200.dp.toPx()) } }
Ruota
Utilizza DrawScope.rotate() per ruotare le operazioni di disegno attorno a un punto di rotazione. Ad esempio, il seguente codice ruota un rettangolo di 45 gradi:
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() per applicare una rotazione all'ambito di disegno corrente, che ruota il rettangolo di 45 gradi.
Interno
Utilizza DrawScope.inset() per regolare i parametri predefiniti dell'attuale
DrawScope, modificando i confini del disegno e traslando i disegni
di conseguenza:
Canvas(modifier = Modifier.fillMaxSize()) { val canvasQuadrantSize = size / 2F inset(horizontal = 50f, vertical = 30f) { drawRect(color = Color.Green, size = canvasQuadrantSize) } }
Questo codice aggiunge efficacemente il padding ai comandi di disegno:
Più trasformazioni
Per applicare più trasformazioni ai disegni, utilizza la funzione DrawScope.withTransform(), che crea e applica una singola trasformazione che combina tutte le modifiche che vuoi. L'utilizzo di withTransform() è più efficiente rispetto alle chiamate nidificate alle singole trasformazioni, perché tutte le trasformazioni vengono eseguite insieme in un'unica operazione, anziché richiedere a Compose di calcolare e salvare ciascuna delle trasformazioni nidificate.
Ad esempio, il seguente codice applica sia una traslazione che una rotazione al rettangolo:
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 per applicare sia una rotazione sia una traslazione, ruotando il rettangolo e spostandolo verso sinistra.Operazioni di disegno comuni
Disegna testo
Per disegnare testo in Compose, in genere puoi utilizzare il componibile Text. Tuttavia, se ti trovi in un DrawScope o vuoi disegnare il testo manualmente con la personalizzazione, puoi utilizzare il metodo DrawScope.drawText().
Per disegnare il testo, crea un TextMeasurer utilizzando rememberTextMeasurer
e chiama drawText con il misuratore:
val textMeasurer = rememberTextMeasurer() Canvas(modifier = Modifier.fillMaxSize()) { drawText(textMeasurer, "Hello") }
Misura il testo
Il disegno del testo funziona in modo leggermente diverso rispetto ad altri comandi di disegno. In genere, al comando di disegno vengono fornite le dimensioni (larghezza e altezza) con cui disegnare la forma o l'immagine. Con il testo, esistono alcuni parametri che controllano le dimensioni del testo visualizzato, come la dimensione del carattere, il carattere, le legature e la spaziatura tra le lettere.
Con Compose, puoi utilizzare un TextMeasurer per accedere alle dimensioni misurate del testo, a seconda dei fattori sopra indicati. Se vuoi disegnare uno sfondo dietro il testo, puoi utilizzare le informazioni misurate per ottenere le dimensioni dell'area occupata dal testo:
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() )
Questo snippet di codice produce uno sfondo rosa sul testo:
La regolazione dei vincoli, delle dimensioni del carattere o di qualsiasi proprietà che influisce sulle dimensioni misurate comporta la generazione di un nuovo report sulle dimensioni. Puoi impostare una dimensione fissa sia per width che per height e il testo segue l'impostazione TextOverflow. Ad esempio, il seguente codice visualizza il testo in ⅓ dell'altezza e ⅓ della larghezza dell'area componibile e imposta TextOverflow su TextOverflow.Ellipsis:
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() )
Il testo viene ora disegnato nei vincoli con un'ellissi alla fine:
TextOverflow.Ellipsis con vincoli fissi sulla misurazione del testo.Disegna immagine
Per disegnare un ImageBitmap con DrawScope, carica l'immagine utilizzando
ImageBitmap.imageResource() e poi chiama drawImage:
val dogImage = ImageBitmap.imageResource(id = R.drawable.dog) Canvas(modifier = Modifier.fillMaxSize(), onDraw = { drawImage(dogImage) })
ImageBitmap su Canvas.Disegnare forme di base
In DrawScope sono disponibili molte funzioni per il disegno di forme. Per disegnare una forma, utilizza una delle funzioni di disegno predefinite, ad esempio drawCircle:
val purpleColor = Color(0xFFBA68C8) Canvas( modifier = Modifier .fillMaxSize() .padding(16.dp), onDraw = { drawCircle(purpleColor) } )
API |
Output |
|
|
|
|
|
|
|
|
|
|
|
|
|
Traccia percorso
Un percorso è una serie di istruzioni matematiche che, una volta eseguite, generano un disegno. DrawScope può disegnare un percorso utilizzando il metodo DrawScope.drawPath().
Ad esempio, supponiamo che tu voglia disegnare un triangolo. Puoi generare un percorso con funzioni come lineTo() e moveTo() utilizzando le dimensioni dell'area di disegno.
Poi, chiama drawPath() con questo percorso appena creato per ottenere un triangolo.
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.Accesso all'oggetto Canvas
Con DrawScope, non hai accesso diretto a un oggetto Canvas. Puoi utilizzare DrawScope.drawIntoCanvas() per accedere all'oggetto Canvas stesso su cui puoi chiamare le funzioni.
Ad esempio, se hai un Drawable personalizzato che vuoi disegnare sul canvas, puoi accedere al canvas e chiamare Drawable#draw(), passando l'oggetto Canvas:
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.Scopri di più
Per saperne di più sul disegno in Compose, consulta le seguenti risorse:
- Modificatori di grafica: scopri i diversi tipi di modificatori di disegno.
- Pennello: scopri come personalizzare la pittura dei tuoi contenuti.
- Layout e grafica personalizzati in Compose - Android Dev Summit 2022: scopri come creare una UI personalizzata in Compose con layout e grafica.
- Esempio di JetLagged: esempio di Compose che mostra come disegnare un grafico personalizzato.
Consigliati per te
- Nota: il testo del link viene visualizzato quando JavaScript è disattivato
- Modificatori di grafica
- Grafica in Compose
- Linee di allineamento in Jetpack Compose