================
ConstraintLayout
================
.. toctree::
:maxdepth: 1
.. contents:: Indice
:local:
:depth: 2
Introduzione
------------
ConstraintLayout consente di creare layout grandi e complessi con una gerarchia di viste piatte (nessun gruppo di viste nidificate). È simile al RelativeLayout in cui tutte le view sono disposte in base alle relazioni tra le view di pari livello e il layout principale, ma è più flessibile del RelativeLayout ed è più facile da usare con il Layout Editor di Android Studio.
Posizionamento relativo
-----------------------
Il posizionamento relativo è uno dei fondamenti di base della creazione di layout in ConstraintLayout. Questi vincoli ti consentono di posizionare un dato widget rispetto a un altro. Puoi vincolare un widget sull'asse orizzontale e verticale:
- Asse orizzontale: sinistra, destra, inizio e fine lati
- Asse verticale: parte superiore, lati inferiori e linea di base del testo
Il concetto generale è di limitare un dato lato di un widget all'altro lato di qualsiasi altro widget.
Ad esempio, per posizionare il pulsante B a destra del pulsante A (Fig. 1):
.. image:: img/constraint/relative-positioning.png
:scale: 50 %
Fig. 1 - Esempio di posizionamento relativo
Dovresti fare:
.. code-block:: Text
Questo dice al sistema che vogliamo che il lato sinistro del pulsante B sia vincolato al lato destro del pulsante A. Un tale vincolo di posizione significa che il sistema cercherà di fare condividere ad entrambi i lati la stessa posizione.
.. image:: img/constraint/relative-positioning-constraints.png
:scale: 50 %
Fig. 2 - Vincoli di posizionamento relativi
Ecco l'elenco dei vincoli disponibili (Figura 2):
- **layout_constraintLeft_toLeftOf**
- **layout_constraintLeft_toRightOf**
- **layout_constraintRight_toLeftOf**
- **layout_constraintRight_toRightOf**
- **layout_constraintTop_toTopOf**
- **layout_constraintTop_toBottomOf**
- **layout_constraintBottom_toTopOf**
- **layout_constraintBottom_toBottomOf**
- **layout_constraintBaseline_toBaselineOf**
- **layout_constraintStart_toEndOf**
- **layout_constraintStart_toStartOf**
- **layout_constraintEnd_toStartOf**
- **layout_constraintEnd_toEndOf**
Prendono tutti un riferimento id ad un altro widget, o il parent(che farà riferimento al contenitore genitore, ovvero il ConstraintLayout):
.. code-block:: Text
Margini
-------
.. image:: img/constraint/relative-positioning-margin.png
:scale: 50 %
Fig. 3 - Margini di posizionamento relativi
Se i margini laterali sono impostati, saranno applicati ai vincoli corrispondenti (se esistono) (Fig. 3), rafforzando il margine come spazio tra il bersaglio e il lato sorgente. I soliti attributi del margine di layout possono essere utilizzati per questo effetto:
- **android:layout_marginStart**
- **android:layout_marginEnd**
- **android:layout_marginLeft**
- **android:layout_marginTop**
- **android:layout_marginRight**
- **android:layout_marginBottom**
Si noti che un margine può essere solo **positivo** o **uguale a zero** e prende un **Dimension**
Margini quando connesso a un GONE widget
''''''''''''''''''''''''''''''''''''''''
Quando una visibilità del target del vincolo di posizione è View.GONE, puoi anche indicare un valore di margine diverso da utilizzare utilizzando i seguenti attributi:
- **layout_goneMarginStart**
- **layout_goneMarginEnd**
- **layout_goneMarginLeft**
- **layout_goneMarginTop**
- **layout_goneMarginRight**
- **layout_goneMarginBottom**
Centraggio posizione
--------------------------------------------
Un aspetto utile di ConstraintLayout è come tratta i vincoli "impossibili". Ad esempio, se abbiamo qualcosa come:
.. code-block:: Text
Comportamento visibilità
------------------------
ConstraintLayout ha una gestione specifica dei widget contrassegnati come **View.GONE**.
I widget **GONE**, come al solito, non verranno visualizzati e non fanno parte del layout stesso (ovvero le loro dimensioni effettive non verranno modificate se contrassegnate come **GONE**).
Ma in termini di calcoli di layout, i widget **GONE** ne fanno ancora parte, con un'importante distinzione:
- Per il passaggio del layout, la loro dimensione sarà considerata pari a zero (in pratica, verranno risolti in un punto)
- Se hanno vincoli ad altri widget, saranno comunque rispettati, ma i margini saranno pari a zero
.. image:: img/constraint/visibility-behavior.png
:scale: 50 %
Fig. 7 - Visibilità
Questo comportamento specifico consente di creare layout in cui è possibile contrassegnare temporaneamente i widget come se fossero **GONE**, senza interrompere il layout (Figura 7), che può essere particolarmente utile quando si eseguono semplici animazioni di layout.
**Nota**: il margine utilizzato sarà il margine che B aveva definito durante il collegamento ad A (vedere la Fig. 7 per un esempio). In alcuni casi, questo potrebbe non essere il margine desiderato (ad esempio A aveva un margine di 100dp sul lato del suo contenitore, B solo un 16dp su A, contrassegnando A come come gone, B avrà un margine di 16dp nel contenitore). Per questo motivo, è possibile specificare un valore di margine alternativo da utilizzare quando la connessione è a un widget contrassegnato come non disponibile (vedere la sezione precedente sugli attributi del margine gone).
Vincoli di dimensione
---------------------
Dimensioni minime su ConstraintLayout
'''''''''''''''''''''''''''''''''''''
È possibile definire le dimensioni minima e massima per la ConstraintLayout stessa:
- **android:minWidth** imposta la larghezza minima per il layout
- **android:minHeight** imposta l'altezza minima per il layout
- **android:maxWidth** imposta la larghezza massima per il layout
- **android:maxHeight** imposta l'altezza massima per il layout
Quelle dimensioni minime e massime saranno usate da ConstraintLayout quando le sue dimensioni sono impostate su **WRAP_CONTENT**
Vincoli di dimensione dei widget
''''''''''''''''''''''''''''''''
La dimensione dei widget può essere specificata impostando gli attributi android:layout_width e android:layout_height in 3 modi diversi:
- Utilizzando una **dimensione specifica** (un valore letterale come 123dp o un riferimento **Dimension**)
- Utilizzando WRAP_CONTENT, che chiederà al widget di calcolare la propria dimensione
- Utilizzando **0dp**, che è l'equivalente di **"MATCH_CONSTRAINT"**
.. image:: img/constraint/dimension-match-constraints.png
:scale: 50 %
Fig. 8 - Vincoli dimensionali
I primi due funzionano in modo simile agli altri layout. L'ultimo ridimensionerà il widget in modo che corrisponda ai vincoli impostati (vedi Fig. 8, (a) è wrap_content, (b) è 0dp). Se i margini sono impostati, saranno presi in considerazione nel calcolo (Fig. 8, (c) con 0dp).
**Importante**: **MATCH_PARENT** non è raccomandato per i widget contenuti in a ConstraintLayout. Un comportamento simile può essere definito usando **MATCH_CONSTRAINT** e i corrispondenti vincoli left / right o top / bottom impostati su "parent".
WRAP_CONTENT: imposizione dei vincoli (aggiunto in 1.1)
'''''''''''''''''''''''''''''''''''''''''''''''''''''''
Se una dimensione è impostata su **WRAP_CONTENT**, nelle versioni precedenti alla 1.1 verranno trattate come una dimensione letterale, ovvero i vincoli non limiteranno la dimensione risultante. Mentre in generale questo è sufficiente (e più veloce), in alcune situazioni, potresti voler usare **WRAP_CONTENT**, ma continuare a imporre vincoli per limitare la dimensione risultante. In tal caso, puoi aggiungere uno degli attributi corrispondenti:
- **app:layout_constrainedWidth="true|false"**
- **app:layout_constrainedHeight="true|false"**
MATCH_CONSTRAINT modificatori dimensioni (aggiunto in 1.1)
''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
Quando una dimensione è impostata su **MATCH_CONSTRAINT**, il comportamento predefinito prevede che le dimensioni risultanti occupino tutto lo spazio disponibile. Sono disponibili diversi modificatori aggiuntivi:
- **layout_constraintWidth_min** e **layout_constraintHeight_min**: imposterà la dimensione minima per questa dimensione
- **layout_constraintWidth_max** e **layout_constraintHeight_max**: imposterà la dimensione massima per questa dimensione
- **layout_constraintWidth_percent** e **layout_constraintHeight_percent**: imposterà la dimensione di questa dimensione come percentuale del genitore
Min e Max
'''''''''
Il valore indicato per min e max può essere una dimensione in Dp o "wrap", che utilizzerà lo stesso valore di ciò che farebbe **WRAP_CONTENT**.
Dimensione percentuale
----------------------
Per utilizzare la percentuale, è necessario impostare quanto segue:
- La dimensione deve essere impostata su **MATCH_CONSTRAINT (0dp)**
- Il valore predefinito deve essere impostato su percent **app:layout_constraintWidth_default="percent"** o **app:layout_constraintHeight_default="percent"**
(**Nota**: questo è necessario in 1.1-beta1 e 1.1-beta2, ma non sarà necessario nelle seguenti versioni se l'attributo percent è definito)
- Quindi imposta gli attributi **layout_constraintWidth_percent** o **layout_constraintHeight_percent** su un valore compreso tra 0 e 1
Ratio (Proporzioni)
-------------------
È anche possibile definire una dimensione di un widget come rapporto con l'altra dimensione. Per fare ciò, è necessario impostare almeno una dimensione vincolata su **0dp** (cioè, **MATCH_CONSTRAINT**) e impostare l'attributo **layout_constraintDimensionRatio** su un dato rapporto. Per esempio:
.. code-block:: Text
imposterà l'altezza del pulsante per essere uguale alla sua larghezza.
Il rapporto può essere espresso come:
- un valore float, che rappresenta un rapporto tra larghezza e altezza
- un rapporto nella forma "larghezza:altezza"
Puoi anche utilizzare il rapporto se entrambe le dimensioni sono impostate su **MATCH_CONSTRAINT** (0dpi). In questo caso il sistema imposta le dimensioni maggiori, soddisfa tutti i vincoli e mantiene le proporzioni specificate. Per vincolare un lato specifico in base alle dimensioni di un altro, è possibile aggiungere W,"o H,per limitare rispettivamente la larghezza o l'altezza. Ad esempio, se una dimensione è vincolata da due destinazioni (ad es. La larghezza è 0 dpi e al centro del genitore) è possibile indicare quale lato deve essere vincolato, aggiungendo la lettera W(per vincolare la larghezza) o H (per vincolare l'altezza) davanti al rapporto, separati da una virgola:
.. code-block:: Text
imposterà l'altezza del pulsante seguendo un rapporto 16:9, mentre la larghezza del pulsante corrisponderà ai vincoli rispetto al genitore.
Catene
------
Le catene forniscono un comportamento di gruppo in un singolo asse (orizzontale o verticale). L'altro asse può essere vincolato in modo indipendente.
Creare una catena
'''''''''''''''''
Un insieme di widget è considerato una catena se sono collegati tra loro tramite una connessione bidirezionale (vedi Fig. 9, che mostra una catena minima, con due widget).
.. image:: img/constraint/chains.png
:scale: 50 %
Fig. 9 - Catena
Teste catena (Chain heads)
''''''''''''''''''''''''''
Le catene sono controllate dagli attributi impostati sul primo elemento della catena (la "testa" della catena):
.. image:: img/constraint/chains-head.png
:scale: 50 %
Fig. 10 - Testa catena
La testa è il widget più a sinistra per le catene orizzontali e il widget più in alto per le catene verticali.
Margini in catene
'''''''''''''''''
Se i margini sono specificati sulle connessioni, saranno presi in considerazione. Nel caso di catene di distribuzione, i margini verranno detratti dallo spazio allocato.
Stile della catena
''''''''''''''''''
Quando si imposta l'attributo **layout_constraintHorizontal_chainStyle** o **layout_constraintVertical_chainStyle** sul primo elemento di una catena, il comportamento della catena cambia in base allo stile specificato (l'impostazione predefinita è **CHAIN_SPREAD**).
- **CHAIN_SPREAD** - gli elementi saranno distribuiti (stile predefinito)
- **Catena ponderata - in modalità CHAIN_SPREAD**, se alcuni widget sono impostati su **MATCH_CONSTRAINT**, divideranno lo spazio disponibile
- **CHAIN_SPREAD_INSIDE** - simile, ma i punti finali della catena non saranno distribuiti
- **CHAIN_PACKED** - gli elementi della catena saranno impacchettati insieme. L'attributo di bias orizzontale o verticale del figlio influenzerà quindi il posizionamento degli elementi impacchettati
.. image:: img/constraint/chains-styles.png
:scale: 50 %
Fig. 11 - Stili delle catene
Catene pesate
'''''''''''''
Il comportamento predefinito di una catena è di distribuire gli elementi in modo uguale nello spazio disponibile. Se uno o più elementi stanno usando **MATCH_CONSTRAINT**, useranno lo spazio vuoto disponibile (equamente diviso tra loro). L'attributo **layout_constraintHorizontal_weight** e **layout_constraintVertical_weight** controllerà come verrà distribuito lo spazio tra gli elementi **MATCH_CONSTRAINT**. Ad esempio, su una catena che contiene due elementi usando **MATCH_CONSTRAINT**, con il primo elemento che usa un peso di 2 e il secondo un peso di 1, lo spazio occupato dal primo elemento sarà il doppio di quello del secondo elemento.
Margini e catene (in 1.1)
'''''''''''''''''''''''''
Quando si usano i margini sugli elementi di una catena, i margini sono additivi.
Ad esempio, su una catena orizzontale, se un elemento definisce un margine destro di 10 dpi e l'elemento successivo definisce un margine sinistro di 5 dpi, il margine risultante tra questi due elementi è 15 dpi.
Un oggetto più i suoi margini sono considerati insieme quando si calcola lo spazio rimanente usato dalle catene per posizionare gli oggetti. Lo spazio rimanente non contiene i margini.
Oggetti di supporto virtuale
----------------------------
Oltre alle funzionalità intrinseche descritte in precedenza, puoi anche utilizzare oggetti di aiuto speciali ConstraintLayout per aiutarti con il layout. Attualmente, l'oggetto Guideline consente di creare linee guida orizzontali e verticali posizionate rispetto al ConstraintLayout contenitore. I widget possono quindi essere posizionati vincolandoli a tali linee guida. In 1.1 sono stati aggiunti anche Barriere e Gruppi.
Guideline
---------
Classe di supporto che rappresenta un oggetto helper della linea guida per ConstraintLayout. Gli oggetti helper non vengono visualizzati sul dispositivo (sono contrassegnati come **View.GONE**) e vengono utilizzati solo per scopi di layout. Funzionano solo all'interno di un ConstraintLayout.
Una linea guida può essere orizzontale o verticale:
- Le linee guida verticali hanno una larghezza pari a zero e l'altezza del loro ConstraintLayout genitore
- Le linee guida orizzontali hanno un'altezza pari a zero e la larghezza del loro ConstraintLayout genitore
Il posizionamento di una linea guida è possibile in tre modi diversi:
- specificando una distanza fissa da sinistra o dall'alto di un layout (**layout_constraintGuide_begin**)
- specificando una distanza fissa dal lato destro o inferiore di un layout (**layout_constraintGuide_end**)
- specificando una percentuale della larghezza o l'altezza di un layout (**layout_constraintGuide_percent**)
I widget possono quindi essere vincolati a una linea guida, consentendo a più widget di essere posizionati facilmente da una linea guida o consentendo un comportamento di layout reattivo utilizzando il posizionamento percentuale.
Vedere l'elenco degli attributi in ConstraintLayout.LayoutParams (sulla documentazione ufficiale) per impostare una linea guida in XML, così come il relativo ConstraintSet.setGuidelineBegin(int, int), ConstraintSet.setGuidelineEnd(int, int) e ConstraintSet.setGuidelinePercent(int, float)funzioni ConstraintSet.
Esempio di Button vincolato a Guideline verticale:
.. code-block:: Text
Barrier (Barriera) (Aggiunto in 1.1)
------------------------------------
Una Barriera (Barrier) fa riferimento a più widget come input e crea una linea guida virtuale basata sul widget più estremo sul lato specificato.
Ad esempio, una barriera sinistra si allineerà a sinistra di tutte le viste referenziate.
Esempio
.. image:: img/constraint/barrier-buttons.png
:scale: 100 %
Abbiamo due pulsanti, @id/button1 e @id/button2. Il campo constraint_referenced_ids farà riferimento ad essi semplicemente disponendoli come elenco separato da virgole:
.. code-block:: Text
.. image:: img/constraint/barrier-start.png
:scale: 100 %
Reversamente, con la direzione (**barrierDirection**) impostata con **end**, avremo:
.. image:: img/constraint/barrier-end.png
:scale: 100 %
Se le dimensioni del widget cambiano, la barriera si sposterà automaticamente in base alla sua direzione per allinearsi al widget più estremo:
.. image:: img/constraint/barrier-adapt.png
:scale: 100 %
Altri widget possono quindi essere vincolati alla barriera stessa, anziché al singolo widget. Ciò consente a un layout di adattarsi automaticamente alle modifiche delle dimensioni dei widget (ad esempio, diverse lingue avranno una lunghezza diversa per parole simili).
Gestione dei widget GONE
''''''''''''''''''''''''
Se la barriera fa riferimento ai widget GONE, il comportamento predefinito consiste nel creare una barriera sulla posizione risolta del widget GONE. Se non si desidera che la barriera tenga conto dei widget GONE, è possibile modificarne il comportamento impostando l'attributo barrierAllowsGoneWidgets su false (impostazione predefinita true).
Gruppo (Group) (Aggiunto in 1.1)
--------------------------------
Questa classe controlla la visibilità di un set di widget referenziati. I widget sono referenziati aggiungendo un elenco di id separati da virgole, ad esempio:
.. code-block:: Text
La visibilità del gruppo verrà applicata ai widget di riferimento. È un modo conveniente per nascondere / mostrare facilmente un set di widget senza dover mantenere questo set a livello di codice.
Gruppi multipli
'''''''''''''''
Più gruppi possono fare riferimento agli stessi widget: in tal caso, l'ordine di dichiarazione XML definirà lo stato di visibilità finale (il gruppo dichiarato per ultimo avrà l'ultima parola).
Ottimizzatore (in 1.1)
----------------------
In 1.1 abbiamo esposto l'ottimizzatore dei vincoli. Puoi decidere quali ottimizzazioni vengono applicate aggiungendo il tag **app:layout_optimizationLevel** all'elemento ConstraintLayout.
- **none (nessuno)** : non vengono applicate ottimizzazioni
- **standard** : predefinito. Ottimizza solo i vincoli diretti e di barriera
- **direct (diretto)** : ottimizza i vincoli diretti
- **barrier (barriera)** : ottimizza i vincoli di barriera
- **chain (catena)** : ottimizzare i vincoli di catena (sperimentale)
- **dimensions (dimensioni)** : ottimizza le misure di quota (sperimentale), riducendo il numero di misure degli elementi di vincoli di corrispondenza
Questo attributo è una maschera, quindi puoi decidere di attivare o disattivare determinate ottimizzazioni elencando quelle che desideri. Ad esempio: app: layout_optimizationLevel = "direct | barrier | chain"
Placeholder (Segnaposto) (Aggiunto in 1.1)
------------------------------------------
Un Placeholder fornisce un oggetto virtuale che può posizionare un oggetto esistente.
Quando l'id di un'altra vista è impostata su un segnaposto (utilizzando setContent()), il segnaposto diventa effettivamente la view del contenuto. Se la view del contenuto esiste sullo schermo, viene considerata come se fosse nascosta dalla sua posizione originale.
La visualizzazione del contenuto viene posizionata utilizzando il layout dei parametri del Placeholder (il campo Placeholder è semplicemente vincolato nel layout come qualsiasi altra vista).
ConstraintSet
-------------
Questa classe consente di definire a livello di programmazione una serie di vincoli da utilizzare con ConstraintLayout. Ti consente di creare e salvare i vincoli e applicarli a un ConstraintLayout esistente. ConstraintsSet può essere creato in vari modi:
- manualmente
c = new ConstraintSet(); c.connect(....);
- da un oggetto R.layout.*
c.clone(context, R.layout.layout1);
- da un ConstraintLayout
c.clone(clayout);
Codice di esempio:
.. code-block:: Text
import android.content.Context;
import android.os.Bundle;
import android.support.constraint.ConstraintLayout;
import android.support.constraint.ConstraintSet;
import android.support.transition.TransitionManager;
import android.support.v7.app.AppCompatActivity;
import android.view.View;
public class MainActivity extends AppCompatActivity {
ConstraintSet mConstraintSet1 = new ConstraintSet(); // crea un Constraint Set
ConstraintSet mConstraintSet2 = new ConstraintSet(); // crea un Constraint Set
ConstraintLayout mConstraintLayout; // cache the ConstraintLayout
boolean mOld = true;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Context context = this;
mConstraintSet2.clone(context, R.layout.state2); // ottieni un constraints da un layout
setContentView(R.layout.state1);
mConstraintLayout = (ConstraintLayout) findViewById(R.id.activity_main);
mConstraintSet1.clone(mConstraintLayout); // ottieni un constraints da un ConstraintSet
}
public void foo(View view) {
TransitionManager.beginDelayedTransition(mConstraintLayout);
if (mOld = !mOld) {
mConstraintSet1.applyTo(mConstraintLayout); // applica nuovo constraints
} else {
mConstraintSet2.applyTo(mConstraintLayout); // applica nuovo constraints
}
}
}