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 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 dei componibili.

Modificatori nell'albero della UI

Per capire in che modo i modificatori si influenzano a vicenda, è utile visualizzare come appaiono nell'albero della UI, che viene generato durante la fase di composizione. Per saperne di più, consulta la sezione Composizione.

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

Codice per composabili e modificatori e la loro rappresentazione visiva come albero della UI.
Figura 1. I modificatori che aggregano i nodi di layout nell'albero della UI.

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

Nella fase di layout, l'algoritmo che attraversa l'albero rimane lo stesso, ma viene visitato anche ogni nodo del modificatore. In questo modo, un modificatore può cambiare i requisiti di dimensione e la posizione del modificatore o del nodo di layout che aggrega.

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

Le implementazioni di Row e Column sono nodi di layout che descrivono come disporre i relativi elementi secondari.

La struttura ad albero di prima, ma ora ogni nodo è solo un layout semplice, con molti nodi di wrapping dei modificatori intorno.
Figura 2. La stessa struttura ad albero della Figura 1, ma con gli elementi componibili nell'albero della UI visualizzati come catene di modificatori.

In sintesi:

  • I modificatori aggregano 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 sul concatenamento dei modificatori e su come influisce sulle dimensioni dei 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. Misurazione degli elementi secondari: un nodo misura i suoi elementi secondari, se presenti.
  2. Decide le proprie dimensioni: in base a queste misurazioni, un nodo decide le proprie dimensioni.
  3. Posiziona i nodi secondari: ogni nodo secondario viene posizionato in base alla posizione del nodo principale.

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, queste devono rientrare in questo intervallo.

Tipi di vincoli

Un vincolo può essere uno dei seguenti:

  • Bounded: il nodo ha una larghezza e un'altezza massime e minime.
Vincoli delimitati di dimensioni diverse all'interno di un container.
Figura 3. Vincoli limitati.
  • Unbounded: il nodo non è vincolato ad alcuna dimensione. I limiti di larghezza e altezza massime sono impostati su infinito.
Vincoli non limitati con larghezza e altezza impostate su infinito. I vincoli si estendono oltre il container.
Figura 4. Vincoli non limitati.
  • Esatta: al nodo viene chiesto di seguire un requisito di dimensione esatto. I limiti minimo e massimo sono impostati sullo stesso valore.
Vincoli esatti che soddisfano un requisito di dimensione esatto 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 container 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 trasmessi da un elemento principale a un elemento secondario.

Come vengono trasmessi i vincoli dall'elemento principale a quello secondario

Durante il primo passaggio dell'algoritmo descritto in Vincoli nella fase di layout, i vincoli vengono trasmessi dall'elemento principale a quello secondario nell'albero della UI.

Quando un nodo principale misura i suoi nodi secondari, fornisce questi vincoli a ciascun nodo secondario per fargli sapere quanto può essere grande o piccolo. Poi, quando decide le proprie dimensioni, rispetta anche i vincoli trasmessi dai suoi elementi principali.

In linea 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 relativi nodi secondari e inoltra gli stessi vincoli al primo nodo secondario.
  2. Se l'elemento secondario è un modificatore che non influisce sulla misurazione, inoltra i vincoli al modificatore successivo. I vincoli vengono trasmessi lungo la catena di modificatori così come sono, a meno che non venga raggiunto un modificatore che influisce sulla misurazione. I vincoli vengono quindi ridimensionati di conseguenza.
  3. Una volta raggiunto un nodo che non ha nodi secondari (noto come "nodo foglia"), decide le sue dimensioni in base ai vincoli passati e restituisce queste dimensioni risolte al nodo principale.
  4. Il genitore adatta i propri vincoli in base alle misurazioni di questo elemento secondario e chiama l'elemento secondario successivo con questi vincoli modificati.
  5. Una volta misurati tutti i nodi secondari di un nodo principale, quest'ultimo 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 approfondito, guarda il video Constraints and modifier order.

Modificatori che influiscono sui vincoli

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

Il modificatore size

Il modificatore size dichiara le dimensioni preferite dei contenuti.

Ad esempio, il seguente albero della UI deve essere visualizzato in un container di 300dp per 200dp. I vincoli sono limitati e consentono 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 limitati impostati dal modificatore di dimensioni in un container.
Figura 7. Vincoli limitati nell'albero della UI e la 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, ma con il modificatore di dimensioni che adatta i vincoli in entrata in modo che corrispondano al valore passato.
Figura 8. Il modificatore size 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:

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

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 eseguirà l'override di questi valori:

Una catena di due modificatori di dimensioni nell'albero della UI e la relativa rappresentazione in un container, 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 esegue l'override del primo valore (100dp).

Il modificatore requiredSize

Utilizza il modificatore requiredSize anziché size se hai bisogno 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 la dimensione viene restituita all'albero, il nodo secondario viene centrato nello spazio disponibile:

Il modificatore size e requiredSize concatenato in un albero della UI e la rappresentazione corrispondente in un container. I vincoli del modificatore requiredSize sostituiscono i vincoli del modificatore size.
Figura 11. Il modificatore requiredSize esegue l'override dei vincoli in entrata dal modificatore size.

I 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 indefinita. Analogamente, con il modificatore height, puoi impostare un'altezza fissa, ma lasciare la larghezza indefinita:

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

Il 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 della UI con il modificatore sizeIn con larghezze e altezze minime e massime impostate e la sua rappresentazione all'interno di un container.
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 container principale.
Figura 14. Image riempie la dimensione massima come risultato della catena di modificatori.
  • 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 dei vincoli 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 trasmessa fino alla parte superiore dell'albero.

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 container principale.
Figura 15. Image è centrato e dimensionato su 50dp.
  • 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. Quindi, mentre fillMaxSize ha generato vincoli fissi, wrapContentSize li reimposta su vincoli limitati. Il nodo successivo 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 la inoltra.
  • Il modificatore wrapContentSize ha una proprietà speciale. Prende il suo elemento secondario e lo posiziona al centro dei limiti minimi disponibili che gli sono stati passati. La dimensione che comunica ai suoi elementi 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 alle dimensioni riportate da Image, quindi il layout con spaziatura interna riporta una larghezza e un'altezza di 120dp.
  • Ora, nella fase di disegno, il modificatore clip agisce su un canvas di 120dp per 120dp. Viene creata una maschera circolare di queste dimensioni.
  • Il modificatore padding inserisce quindi i suoi contenuti di 10dp su tutti i lati, il che riduce le dimensioni del canvas per Image a 100dp per 100dp.
  • L'elemento Image viene disegnato in quel canvas più piccolo. L'immagine viene ritagliata in base al cerchio originale di 120dp, quindi l'output è un risultato non rotondo.