Molte app devono essere in grado di controllare con precisione cosa viene disegnato sullo schermo. Potrebbe trattarsi di un semplice riquadro o cerchio sullo schermo nel punto giusto oppure di una complessa disposizione di elementi grafici in molti stili diversi.
Disegno di base con modificatori e DrawScope
Il modo principale per disegnare qualcosa di personalizzato in Compose è con i 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 ti serve solo un composable che disegni, puoi utilizzare il composable
Canvas. Il componente componibile Canvas è un
wrapper pratico per Modifier.drawBehind. Posiziona Canvas nel layout come faresti con qualsiasi altro elemento dell'interfaccia utente di Compose. All'interno di
Canvas, puoi disegnare elementi con un controllo preciso su stile e
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 attuali di DrawScope.
Per disegnare qualcosa, puoi utilizzare una delle tante funzioni di disegno su 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 grafici.
Sistema di coordinate
Per disegnare qualcosa sullo schermo, devi conoscere l'offset (x e y) e le dimensioni
dell'elemento. Con molti dei metodi di disegno su DrawScope, la posizione e le dimensioni
sono fornite dai valori predefiniti dei parametri. I parametri predefiniti in genere
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. Puoi notare
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 dell'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 iniziale e finale 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.
Diffusione
Utilizza
DrawScope.scale()
per aumentare le dimensioni delle operazioni di disegno di un fattore. Operazioni come
scale() si applicano a tutte le operazioni di disegno all'interno della 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, il basso, sinistra o 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
Usa
DrawScope.rotate()
per ruotare le operazioni di disegno intorno 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.
Inset
Utilizza DrawScope.inset() per regolare i parametri predefiniti dell'attuale
DrawScope, modificando i limiti di 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 in modo efficace 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 apportare. L'utilizzo di
withTransform() è più efficiente rispetto all'esecuzione di chiamate nidificate a singole
trasformazioni, perché tutte le trasformazioni vengono eseguite insieme in una
singola operazione, anziché Compose che deve 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 che una traslazione, ruotando il rettangolo e spostandolo a sinistra.Operazioni di disegno comuni
Disegna testo
Per disegnare il testo in Compose, in genere puoi utilizzare il composable 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 lo strumento di misurazione:
val textMeasurer = rememberTextMeasurer() Canvas(modifier = Modifier.fillMaxSize()) { drawText(textMeasurer, "Hello") }
Misura del testo
Il testo del disegno funziona in modo leggermente diverso rispetto ad altri comandi di disegno. Normalmente, dai al comando di disegno le dimensioni (larghezza e altezza) con cui disegnare la forma/l'immagine. Con il testo, esistono alcuni parametri che controllano le dimensioni del testo di rendering, come dimensioni del carattere, carattere, legature e 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 modifica dei vincoli, delle dimensioni del carattere o di qualsiasi proprietà che influisce sulle dimensioni misurate
comporta la segnalazione di una nuova dimensione. Puoi impostare una dimensione fissa sia per width
che per height e il testo segue poi la TextOverflow impostata. Ad
esempio, il seguente codice esegue il rendering del 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
DrawScope offre molte funzioni di 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 |
|
|
|
|
|
|
|
|
|
|
|
|
|
Percorso di disegno
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 sulla tela, puoi accedere alla tela 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 ulteriori informazioni su Drawing in Compose, consulta le seguenti risorse:
- Modificatori grafici: scopri i diversi tipi di modificatori di disegno.
- Pennello: scopri come personalizzare la pittura dei tuoi contenuti.
- Layout e grafiche personalizzati in Compose - Android Dev Summit 2022 - Scopri come creare una UI personalizzata in Compose con layout e grafiche.
- JetLagged Sample: componi un campione che mostri come disegnare un grafico personalizzato.
Consigliati per te
- Nota: il testo del link viene visualizzato quando JavaScript è disattivato
- Modificatori di grafica
- Grafica in Composizione
- Linee di allineamento in Jetpack Compose