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 agli elementi componibili, 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 degli elementi componibili.

Modificatori nella struttura dell'interfaccia utente

Per capire come i modificatori si influenzano a vicenda, è utile visualizzare il modo in cui appaiono nella struttura dell'interfaccia utente, che viene generata durante la fase di composizione. Per ulteriori informazioni, consulta la sezione Composizione.

Nella struttura dell'interfaccia utente, puoi visualizzare i modificatori come nodi wrapper per i nodi di layout:

Codice per i composable e i modificatori e la loro rappresentazione visiva come albero dell'interfaccia utente.
Figura 1. Modificatori che racchiudono i nodi di layout nella struttura dell'interfaccia utente.

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

Nella fase di layout, l'algoritmo che percorre la struttura rimane lo stesso, ma viene visitato anche ogni nodo modificatore. In questo modo, un modificatore può modificare i requisiti di dimensione e il posizionamento del modificatore o del nodo di layout che racchiude.

Come mostrato nella Figura 2, l'implementazione degli elementi componibili Image e Text è costituita da una catena di modificatori che racchiudono un singolo nodo di layout.

Le implementazioni di Row e Column sono nodi di layout che descrivono come disporre i relativi nodi 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 gli elementi componibili nella struttura dell'interfaccia utente visualizzati come catene di modificatori.

In sintesi:

  • I modificatori racchiudono un singolo modificatore o nodo di 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 degli elementi 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 nodi secondari: un nodo misura i relativi nodi secondari, se presenti.
  2. Decidi le dimensioni: in base a queste misurazioni, un nodo decide le proprie dimensioni.
  3. Posiziona i nodi secondari: ogni nodo secondario viene posizionato rispetto alla posizione del nodo.

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 di dimensioni.

Tipi di vincoli

Un vincolo può essere uno dei seguenti:

  • Limitato: 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.
  • Illimitato: 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 illimitati.
  • Esatto: al nodo viene chiesto di rispettare un requisito di dimensione esatta. I limiti minimi e massimi 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 limitati e illimitati e larghezze e altezze esatte.

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

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

Durante il primo passaggio dell'algoritmo descritto in Vincoli nella fase di layout, i vincoli vengono passati dall'elemento principale all'elemento secondario nella struttura dell'interfaccia utente.

Quando un nodo principale misura i relativi nodi secondari, fornisce questi vincoli a ogni nodo secondario per comunicare le dimensioni massime e minime consentite. Poi, quando decide le proprie dimensioni, rispetta anche i vincoli passati dai relativi elementi principali.

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

  1. Per decidere le dimensioni che vuole effettivamente occupare, il nodo radice nella struttura dell'interfaccia utente misura i relativi nodi secondari e inoltra gli stessi vincoli al primo nodo secondario.
  2. Se il nodo secondario è un modificatore che non influisce sulla misurazione, inoltra i vincoli al modificatore successivo. I vincoli vengono passati alla catena di modificatori così come sono, finché non viene raggiunto un modificatore che influisce sulla misurazione. Le dimensioni dei vincoli vengono quindi modificate di conseguenza.
  3. Una volta raggiunto un nodo che non ha nodi secondari (denominato "nodo foglia"), questo decide le proprie dimensioni in base ai vincoli passati e restituisce queste dimensioni risolte al relativo nodo principale.
  4. Il nodo principale adatta i propri vincoli in base alle misurazioni di questo nodo secondario e chiama il nodo secondario successivo con questi vincoli modificati.
  5. Una volta misurati tutti i nodi secondari di un nodo principale, il nodo principale decide le proprie dimensioni e le comunica al relativo nodo principale.
  6. In questo modo, l'intera struttura viene attraversata in profondità. Alla fine, tutti i nodi hanno deciso le proprie dimensioni e il passaggio di misurazione è completato.

Per un esempio dettagliato, guarda il video Vincoli e ordine dei modificatori.

Modificatori che influiscono sui vincoli

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

Il modificatore size

Il modificatore size dichiara le dimensioni preferite dei contenuti.

Ad esempio, la seguente struttura dell'interfaccia utente deve essere sottoposta a rendering in un contenitore di 300dp per 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 limitati nella struttura dell'interfaccia utente e relativa rappresentazione in un contenitore.

Il modificatore size adatta i vincoli in entrata in modo che corrispondano al valore passato. 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 di vincolo più piccolo o superiori al limite di vincolo più grande, il modificatore corrisponde ai vincoli passati il più possibile, rispettando comunque i vincoli passati:

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 la concatenazione di più modificatori size non funziona. Il primo modificatore size imposta i vincoli minimi e massimi su un valore fisso. Anche se il secondo modificatore size 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 dimensioni 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 (50dp) non sostituisce il primo valore (100dp).

Il modificatore requiredSize

Utilizza il modificatore requiredSize anziché size se devi fare in modo che il tuo nodo sostituisca i vincoli in entrata. Il modificatore requiredSize sostituisce i vincoli in entrata e passa la dimensione specificata come limiti esatti.

Quando la dimensione viene passata di nuovo alla struttura, il nodo secondario verrà centrato nello spazio disponibile:

Il modificatore size e requiredSize concatenato in un 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 sostituisce i vincoli in entrata dal modificatore size.

I modificatori width e height

Il modificatore size adatta sia la larghezza sia l'altezza dei vincoli. Con il modificatore width, puoi impostare una larghezza fissa, ma lasciare l'altezza indecisa. Allo stesso modo, con il modificatore height, puoi impostare un'altezza fissa, ma lasciare la larghezza indecisa:

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. Il modificatore width e il modificatore height che impostano rispettivamente una larghezza e un'altezza fisse.

Il modificatore sizeIn

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

Un albero UI 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 minWidth, maxWidth, minHeight e maxHeight impostati.

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:

Un quadrato blu che riempie il contenitore principale.
Figura 14. The Image riempie la dimensione massima a seguito della catena di modificatori.
  • Il modificatore fillMaxSize modifica i vincoli in modo da impostare sia la larghezza sia l'altezza minime sul valore massimo: 300dp di larghezza e 200dp di 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 di vincolo esatti di 300 per 200, ignorando di fatto il valore fornito nel modificatore size.
  • Image segue questi limiti e segnala una dimensione di 300 per 200, che viene passata fino alla struttura.

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

Questo snippet produce il seguente output:

Un piccolo quadrato blu centrato all'interno del contenitore principale.
Figura 15. La Image è centrata e dimensionata su 50dp.
  • Il modificatore fillMaxSize adatta i vincoli in modo da impostare sia la larghezza sia l'altezza minime sul valore massimo: 300dp di larghezza e 200dp di altezza.
  • Il modificatore wrapContentSize reimposta i vincoli minimi. Pertanto, mentre fillMaxSize ha generato vincoli fissi, wrapContentSize li reimposta sui vincoli limitati. 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 minimi e massimi di 50.
  • Image si risolve in una dimensione di 50 per 50 e il modificatore size la inoltra.
  • Il wrapContentSize modificatore ha una proprietà speciale. Prende il nodo secondario e lo posiziona al centro dei limiti minimi disponibili che gli sono stati passati. La dimensione che comunica ai relativi nodi principali è quindi uguale ai limiti minimi che gli sono stati passati.

Combinando solo tre modificatori, puoi definire una dimensione per l'elemento 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:

Una forma circolare ritagliata in modo errato a causa
       dell'ordine dei modificatori.
Figura 16. Una forma ritagliata in modo errato a causa dell'ordine dei modificatori.
  • 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 segnala una dimensione di 100dp per 100dp.
  • Il modificatore padding aggiunge 10dp su tutti i lati alla dimensione segnalata da Image, quindi il layout con il padding segnala una larghezza e un'altezza di 120dp.
  • Ora, nella fase di disegno, il modificatore clip agisce su un canvas di 120dp per 120dp. Crea una maschera circolare di queste dimensioni.
  • Il modificatore padding inserisce quindi i contenuti di 10dp su tutti i lati, il che riduce le dimensioni del canvas per Image a 100dp per 100dp.
  • Image viene disegnata in questo canvas più piccolo. L'immagine viene ritagliata in base al cerchio originale di 120dp, quindi l'output è un risultato non rotondo.