Vincoli e ordine dei modificatori

In Compose, puoi concatenare più modificatori per cambiare l'aspetto di un elemento componibile. Queste catene di modificatori possono influire sui vincoli passati ai composable, che definiscono i limiti di larghezza e altezza.

Questa pagina descrive in che modo i modificatori concatenati influiscono sui vincoli e, di conseguenza, sulla misurazione e sul posizionamento dei componenti componibili.

Modificatori nell'albero della UI

Per capire come si influenzano a vicenda i modificatori, è utile visualizzare il loro aspetto nell'albero dell'interfaccia utente, che viene generato durante la fase di composizione. Per maggiori informazioni, consulta la sezione Composizione.

Nell'albero della UI, puoi visualizzare i modificatori come nodi wrapper per i nodi del layout:

Codice per i componenti componibili e i modificatori e la loro rappresentazione visiva come albero dell'interfaccia utente.
Figura 1. Modificatori che racchiudono i nodi di layout nell'albero della UI.

L'aggiunta di più di un modificatore a un elemento componibile crea una catena di modificatori. Quando concateni più modificatori, ogni nodo modificatore contiene il resto della catena e il nodo di layout all'interno. Ad esempio, quando concateni un modificatore clip e un modificatore size, il nodo modificatore clip racchiude il nodo modificatore size, che a sua volta racchiude il nodo layout Image.

Nella fase di layout, l'algoritmo che percorre la struttura ad albero rimane lo stesso, ma viene visitato anche ogni nodo modificatore. In questo modo, un modificatore può cambiare le dimensioni e il posizionamento del modificatore o del nodo di layout che racchiude.

Come mostrato nella Figura 2, l'implementazione dei composable Image e Text consiste in una catena di modificatori che racchiudono un singolo nodo di layout. Le implementazioni di Row e Column sono semplicemente nodi di layout che descrivono come disporre i relativi elementi secondari.

La struttura ad albero di prima, ma ora ogni nodo è solo un semplice layout, con molti nodi di wrapping dei modificatori intorno.
Figura 2. La stessa struttura ad albero della Figura 1, ma con i composable nell'albero dell'interfaccia utente visualizzati come catene di modificatori.

Per riassumere:

  • I modificatori racchiudono un singolo nodo di modificatore o layout.
  • I nodi di layout possono disporre più nodi secondari.

Le sezioni seguenti descrivono come utilizzare questo modello mentale per ragionare sulla concatenazione dei modificatori e su come influisce sulle dimensioni dei componenti componibili.

Vincoli nella fase di layout

La fase di layout segue un algoritmo in tre passaggi per trovare la larghezza, l'altezza e le coordinate x e y di ogni nodo di layout:

  1. Misura i figli: un nodo misura i suoi figli, se presenti.
  2. Decide own size: in base a queste misurazioni, un nodo decide le proprie dimensioni.
  3. Posiziona figli: ogni nodo figlio viene posizionato rispetto alla posizione del nodo stesso.

Constraints aiutano a trovare le dimensioni giuste per i nodi durante i primi due passaggi dell'algoritmo. I vincoli definiscono i limiti minimi e massimi per la larghezza e l'altezza di un nodo. Quando il nodo decide le sue dimensioni, le dimensioni misurate devono rientrare in questo intervallo.

Tipi di vincoli

Un vincolo può essere uno dei seguenti:

  • Delimitato: il nodo ha una larghezza e un'altezza massime e minime.
Vincoli delimitati di dimensioni diverse all'interno di un contenitore.
Figura 3. Vincoli limitati.
  • Senza limiti: il nodo non è vincolato a nessuna dimensione. I limiti massimi di larghezza e altezza sono impostati su infinito.
Vincoli illimitati con larghezza e altezza impostate su infinito. I vincoli si estendono oltre il contenitore.
Figura 4. Vincoli senza limiti.
  • Esatta: al nodo viene chiesto di rispettare un requisito di dimensione esatta. I limiti minimo e massimo sono impostati sullo stesso valore.
Vincoli esatti che rispettano un requisito di dimensione esatta all'interno del contenitore.
Figura 5. Vincoli esatti.
  • Combinazione: il nodo segue una combinazione dei tipi di vincoli precedenti. Ad esempio, un vincolo potrebbe limitare la larghezza consentendo un'altezza massima illimitata oppure impostare una larghezza esatta, ma fornire un'altezza limitata.
Due contenitori che mostrano combinazioni di vincoli limitati e illimitati e larghezze e altezze esatte.
Figura 6. Combinazioni di vincoli con limiti e senza limiti e larghezze e altezze esatte.

La sezione successiva descrive come questi vincoli vengono trasmessi da un elemento principale a un elemento secondario.

Come vengono passati i vincoli dall'elemento principale all'elemento secondario

Durante il primo passaggio dell'algoritmo descritto nella fase Vincoli nel layout, i vincoli vengono trasmessi dal genitore al figlio nell'albero dell'interfaccia utente.

Quando un nodo genitore misura i suoi figli, fornisce questi vincoli a ciascun figlio per fargli sapere quanto può essere grande o piccolo. Poi, quando decide le proprie dimensioni, rispetta anche i vincoli trasmessi dai propri genitori.

A livello generale, l'algoritmo funziona nel seguente modo:

  1. Per decidere le dimensioni che vuole effettivamente occupare, il nodo principale nell'albero della UI misura i suoi figli e inoltra gli stessi vincoli al suo primo figlio.
  2. Se il bambino è un modificatore che non influisce sulla misurazione, inoltra i vincoli al modificatore successivo. I vincoli vengono trasmessi alla catena di modificatori così come sono, a meno che non venga raggiunto un modificatore che influisce sulla misurazione. Le limitazioni vengono quindi ridimensionate di conseguenza.
  3. Una volta raggiunto un nodo senza figli (chiamato "nodo foglia"), ne determina le dimensioni in base ai vincoli passati e restituisce le dimensioni risolte al nodo padre.
  4. Il genitore adatta i propri vincoli in base alle misurazioni del figlio e chiama il figlio successivo con questi vincoli modificati.
  5. Una volta misurati tutti i figli di un nodo principale, il nodo principale decide le proprie dimensioni e le comunica al proprio nodo principale.
  6. In questo modo, l'intero albero viene attraversato in profondità. Alla fine, tutti i nodi hanno deciso le loro dimensioni e il passaggio di misurazione è completato.

Per un esempio dettagliato, guarda il video Constraints and modifier order.

Modificatori che influiscono sui vincoli

Nella sezione precedente hai appreso che alcuni modificatori possono influire sulle dimensioni del vincolo. Le sezioni seguenti descrivono modificatori specifici che influiscono sui vincoli.

Modificatore size

Il modificatore size dichiara le dimensioni preferite dei contenuti.

Ad esempio, il seguente albero dell'interfaccia utente deve essere visualizzato in un contenitore di 300dp da 200dp. I vincoli sono limitati, consentendo larghezze comprese tra 100dp e 300dp e altezze comprese tra 100dp e 200dp:

Una parte di un albero della UI con il modificatore di dimensioni che racchiude un nodo di layout e la
  rappresentazione dei vincoli delimitati impostati dal modificatore di dimensioni in un contenitore.
Figura 7. Vincoli delimitati nell'albero della UI e relativa rappresentazione in un contenitore.

Il modificatore size adatta i vincoli in entrata in modo che corrispondano al valore che gli viene trasmesso. In questo esempio, il valore è 150dp:

Come nella Figura 7, tranne per il fatto che il modificatore delle dimensioni adatta i vincoli in entrata in modo che corrispondano al valore passato.
Figura 8. Il modificatore size che regola i vincoli su 150dp.

Se la larghezza e l'altezza sono inferiori al limite del vincolo più piccolo o superiori al limite del vincolo più grande, il modificatore corrisponde ai vincoli passati nel modo più preciso possibile, rispettando comunque i vincoli passati in:

Due alberi UI e le relative rappresentazioni nei contenitori. Nel primo, il modificatore di dimensioni accetta i vincoli in entrata; nel secondo, il modificatore di dimensioni si adatta il più possibile ai vincoli troppo grandi, ottenendo vincoli che riempiono il contenitore.
Figura 9. Il modificatore size che rispetta il vincolo passato il più possibile.

Tieni presente che il concatenamento di più modificatori size non funziona. Il primo modificatore size imposta i vincoli minimo e massimo su un valore fisso. Anche se il secondo modificatore di dimensioni richiede una dimensione più piccola o più grande, deve comunque rispettare i limiti esatti passati, quindi non sostituirà questi valori:

Una catena di due modificatori di dimensione nell'albero dell'interfaccia utente e la relativa rappresentazione in un contenitore,
  che è il risultato del primo valore passato e non del secondo.
Figura 10. Una catena di due modificatori size, in cui il secondo valore passato in (50dp) non sostituisce il primo valore (100dp).

Modificatore requiredSize

Utilizza il modificatore requiredSize anziché size se vuoi che il nodo esegua l'override dei vincoli in entrata. Il modificatore requiredSize sostituisce i vincoli in entrata e passa le dimensioni specificate come limiti esatti.

Quando le dimensioni vengono ripassate nella struttura, il nodo secondario viene centrato nello spazio disponibile:

Il modificatore size e requiredSize concatenato in una struttura ad albero dell'interfaccia utente e la rappresentazione
  corrispondente in un contenitore. I vincoli del modificatore requiredSize sostituiscono i vincoli del modificatore
  size.
Figura 11. Il modificatore requiredSize che esegue l'override dei vincoli in entrata dal modificatore size.

Modificatori width e height

Il modificatore size adatta sia la larghezza che l'altezza dei vincoli. Con il modificatore width, puoi impostare una larghezza fissa, ma lasciare l'altezza non definita. Analogamente, con il modificatore height puoi impostare un'altezza fissa, ma lasciare la larghezza non decisa:

Due alberi UI, uno con il modificatore di larghezza e la relativa rappresentazione del contenitore e l'altro
  con il modificatore di altezza e la relativa rappresentazione.
Figura 12. I modificatori width e height impostano una larghezza e un'altezza fisse, rispettivamente.

Modificatore sizeIn

Il modificatore sizeIn consente di impostare vincoli minimi e massimi esatti per larghezza e altezza. Utilizza il modificatore sizeIn se hai bisogno di un controllo granulare sui vincoli.

Un albero dell'interfaccia utente con il modificatore sizeIn con larghezze e altezze minime e massime impostate
  e la relativa rappresentazione all'interno di un contenitore.
Figura 13. Il modificatore sizeIn con i set minWidth, maxWidth, minHeight e maxHeight.

Esempi

Questa sezione mostra e spiega l'output di diversi snippet di codice con modificatori concatenati.

Image(
    painterResource(R.drawable.hero),
    contentDescription = null,
    Modifier
        .fillMaxSize()
        .size(50.dp)
)

Questo snippet produce il seguente output:

  • Il modificatore fillMaxSize modifica i vincoli per impostare sia la larghezza che l'altezza minime sul valore massimo: 300dp in larghezza e 200dp in altezza.
  • Anche se il modificatore size vuole utilizzare una dimensione di 50dp, deve comunque rispettare i vincoli minimi in entrata. Pertanto, il modificatore size restituirà anche i limiti esatti del vincolo 300 per 200, ignorando di fatto il valore fornito nel modificatore size.
  • Image segue questi limiti e segnala una dimensione di 300 x 200, che viene trasmessa fino in cima all'albero.

Image(
    painterResource(R.drawable.hero),
    contentDescription = null,
    Modifier
        .fillMaxSize()
        .wrapContentSize()
        .size(50.dp)
)

Questo snippet produce il seguente output:

  • Il modificatore fillMaxSize adatta i vincoli per impostare sia la larghezza che l'altezza minime sul valore massimo: 300dp in larghezza e 200dp in altezza.
  • Il modificatore wrapContentSize reimposta i vincoli minimi. Pertanto, mentre fillMaxSize ha comportato vincoli fissi, wrapContentSize li reimposta su vincoli delimitati. Il nodo seguente può ora occupare di nuovo l'intero spazio o essere più piccolo dell'intero spazio.
  • Il modificatore size imposta i vincoli sui limiti minimo e massimo di 50.
  • Image viene risolto in una dimensione di 50 per 50 e il modificatore size lo inoltra.
  • Il modificatore wrapContentSize ha una proprietà speciale. Prende il figlio e lo posiziona al centro dei limiti minimi disponibili che gli sono stati trasmessi. La dimensione comunicata ai genitori è quindi uguale ai limiti minimi che sono stati passati.

Combinando solo tre modificatori, puoi definire una dimensione per il componente componibile e centrarlo nel relativo elemento principale.

Image(
    painterResource(R.drawable.hero),
    contentDescription = null,
    Modifier
        .clip(CircleShape)
        .padding(10.dp)
        .size(100.dp)
)

Questo snippet produce il seguente output:

  • Il modificatore clip non modifica i vincoli.
    • Il modificatore padding riduce i vincoli massimi.
    • Il modificatore size imposta tutti i vincoli su 100dp.
    • Image rispetta questi vincoli e riporta una dimensione di 100 per 100dp.
    • Il modificatore padding aggiunge 10dp a tutte le dimensioni, quindi aumenta la larghezza e l'altezza riportate di 20dp.
    • Ora, nella fase di disegno, il modificatore clip agisce su un canvas di 120 x 120dp. Quindi, crea una maschera circolare di quelle dimensioni.
    • Il modificatore padding inserisce i contenuti di 10dp su tutte le dimensioni, quindi riduce le dimensioni del canvas a 100 di 100dp.
    • Il Image viene disegnato nel canvas. L'immagine viene ritagliata in base al cerchio originale di 120dp, quindi l'output è un risultato non rotondo.