Scopriamo cos'è ConstraintLayout — un sistema di posizionamento flessibile per Android che consente di creare gerarchie piatte di view usando constraint (vincoli) invece di LinearLayout e RelativeLayout annidati. ConstraintLayout risolve il problema dell'"annidamento infernale" (layout nesting hell), riducendo la profondità della gerarchia a un singolo livello e accelerando il rendering dello schermo. La libreria fa parte di Jetpack ed è disponibile da Android 2.3 (API 9) tramite la support-library. I meccanismi principali sono descritti nella documentazione ufficiale di Android.
Punti chiave
ConstraintLayout è un ViewGroup della libreria AndroidX ConstraintLayout, progettato per creare interfacce flessibili e performanti attraverso constraint dichiarativi. A differenza di LinearLayout, che dispone gli elementi in linea, o RelativeLayout, che posiziona gli elementi rispetto ai vicini, ConstraintLayout consente di ancorare ciascun elemento rispetto a qualsiasi altro elemento e al genitore contemporaneamente.
La libreria è stata annunciata al Google I/O 2016 come soluzione per accelerare il rendering di schermate complesse. Il problema chiave che ConstraintLayout risolve è l'annidamento dei layout. Ogni ViewGroup annidato aggiunge almeno due passaggi di measure e un passaggio di layout. Una schermata con 4 livelli di annidamento esegue 8 passaggi di measure; ConstraintLayout con la stessa funzionalità esegue solo 2 passaggi. Secondo Google (Android Performance Blog, 2017), sostituire tre LinearLayout annidati con un ConstraintLayout riduce il tempo di onMeasure del 40%.
La versione attuale ConstraintLayout 2.1.4 funziona stabilmente su Android 2.3+ (API 9) tramite AndroidX. La versione 2.0 ha introdotto il posizionamento circolare, Flow (avvolgimento automatico degli elementi) e il supporto per MotionLayout. ConstraintLayout è essenziale per comprendere lo sviluppo Android moderno — viene utilizzato in Jetpack Compose come concetto base dei modificatori, nei modelli predefiniti di Android Studio e in Material Design 3.
La gerarchia piatta di ConstraintLayout significa che tutte le viste figlie sono allo stesso livello di annidamento. Invece di posizionare l'elemento A in un LinearLayout, e il LinearLayout in un RelativeLayout, tutti gli elementi vengono collegati direttamente al ConstraintLayout genitore o tra loro tramite attributi. Questo offre: minor consumo di memoria (ogni ViewGroup è un oggetto nell'heap Java), passaggio di layout più veloce (meno chiamate ricorsive) e comportamento più prevedibile quando le dimensioni dello schermo cambiano.
Un constraint (vincolo) è una connessione tra il bordo di una View (o il suo centro) e il bordo di un'altra View o del genitore. Ogni View può avere fino a 8 constraint: left, top, right, bottom, start, end, baseline e center. Come minimo, due constraint perpendicolari sono sufficienti per il posizionamento (ad esempio, top + left).
Formato dell'attributo: app:layout_constraint[Source]_to[Target]Of="[id]" — dove Source è il bordo collegato (Left, Right, Top, Bottom, Start, End, Baseline) e Target è il bordo di destinazione. Esempio: app:layout_constraintTop_toBottomOf="@+id/header" significa "il bordo superiore dell'elemento corrente è collegato al bordo inferiore dell'elemento header". Per collegarsi al genitore, si usa l'id parent.
Il bias (inclinazione) è un parametro che funziona in presenza di constraint opposti (left + right o top + bottom). I valori vanno da 0 a 1: 0 — premuto verso il bordo sinistro/superiore, 0.5 — centrato, 1 — verso il bordo destro/inferiore. Attributi: layout_constraintHorizontal_bias (0.0–1.0) e layout_constraintVertical_bias. I margini vengono impostati con i android:layout_margin* standard, ma constraint e margini funzionano indipendentemente: il margine è un offset dal constraint, non dalla View vicina.
A partire da ConstraintLayout 1.1+, è stato aggiunto il supporto per dimensioni percentuali tramite layout_constraintWidth_percent e layout_constraintHeight_percent. Un valore di 0.3 significa 30% della larghezza/altezza del genitore. Combinato con il bias, questo consente di creare layout adattivi senza programmazione.
Una Chain (catena) è un gruppo di due o più View collegate da constraint bidirezionali (A è collegato a B, B è collegato ad A). Le catene distribuiscono automaticamente lo spazio tra gli elementi secondo una delle modalità: spread (uniformemente, tenendo conto dei margini), spread_inside (uniformemente, elementi esterni senza margine di bordo), packed (elementi compressi con un bias comune). La modalità viene impostata tramite l'attributo app:layout_constraintHorizontal_chainStyle o layout_constraintVertical_chainStyle.
Una Guideline (guida) è una View ausiliaria, invisibile in fase di esecuzione, che definisce una linea per il collegamento. Una Guideline può essere orizzontale o verticale, posizionata in dp, percentuali (app:layout_constraintGuide_percent) o con un offset dal bordo (app:layout_constraintGuide_begin/end). Le Guideline sono indispensabili per layout adattivi — ad esempio, per dividere lo schermo in due metà uguali indipendentemente dalle dimensioni del dispositivo.
Secondo Google I/O 2017, le catene con spread_inside sono più performanti del 15–20% rispetto ai LinearLayout annidati con weight, poiché evitano il doppio passaggio di measure necessario per il calcolo del peso.
Una Barrier (barriera) è una View virtuale che regola dinamicamente la sua posizione in base alla dimensione di un gruppo di elementi. A differenza di una Guideline con posizione fissa, una Barrier viene "spinta" dall'elemento più largo del gruppo. Ad esempio, se hai un titolo e una descrizione con lunghezze sconosciute, una Barrier collegata al bordo destro del testo più largo consente di posizionare un'icona subito dopo. Attributi: app:barrierDirection (left, right, top, bottom, start, end) e app:constraint_referenced_ids (elenco di id separati da virgole).
Group è un contenitore virtuale che gestisce la visibilità di più View contemporaneamente. Invece di chiamare setVisibility per ogni singolo elemento, è sufficiente modificare la visibilità di un Group. Group non influisce sul posizionamento — solo sulla visibilità. Flow è un aiutante virtuale per creare layout "fluidi": gli elementi vengono automaticamente spostati su una nuova riga/colonna quando lo spazio scarseggia, come il testo in un paragrafo. Flow supporta wrapMode: none, chain e aligned.
Questi strumenti (Barrier, Group, Flow, Guideline) sono chiamati aiutanti virtuali perché non sono View in senso classico — non occupano spazio nella gerarchia e non partecipano a eventi di focus o tocco. Il loro scopo è semplificare la manutenzione di layout complessi senza aggiungere contenitori annidati.
Un semplice modulo di accesso con un campo email, un campo password e un pulsante. Tutti gli elementi sono collegati al genitore, tranne il pulsante — si trova sotto il campo password. Viene utilizzata una gerarchia piatta — tutti e tre gli elementi sono allo stesso livello.
<androidx.constraintlayout.widget.ConstraintLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent">
<com.google.android.material.textfield.TextInputLayout
android:id="@+id/email_input"
android:layout_width="0dp"
android:layout_height="wrap_content"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"
android:layout_marginTop="32dp"
android:layout_marginHorizontal="16dp" />
<com.google.android.material.textfield.TextInputLayout
android:id="@+id/password_input"
android:layout_width="0dp"
android:layout_height="wrap_content"
app:layout_constraintTop_toBottomOf="@+id/email_input"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"
android:layout_marginTop="16dp"
android:layout_marginHorizontal="16dp" />
<Button
android:id="@+id/login_button"
android:layout_width="0dp"
android:layout_height="wrap_content"
app:layout_constraintTop_toBottomOf="@+id/password_input"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"
android:layout_marginTop="24dp"
android:layout_marginHorizontal="16dp"
android:text="Accedi" />
</androidx.constraintlayout.widget.ConstraintLayout>
Tutti gli elementi hanno una larghezza di 0dp (match_constraint), il che significa che si estendono dal constraint start a end tenendo conto dei margini orizzontali. Ciò equivale a match_parent con margini, ma senza annidamento.
Tre pulsanti distribuiti uniformemente in orizzontale con margini di bordo. La catena spread_inside posiziona i pulsanti esterni ai bordi e quello centrale centrato tra di loro.
<Button
android:id="@+id/btn_left"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toLeftOf="@+id/btn_center"
android:text="Sinistra" />
<Button
android:id="@+id/btn_center"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:layout_constraintLeft_toRightOf="@+id/btn_left"
app:layout_constraintRight_toLeftOf="@+id/btn_right"
android:text="Centro" />
<Button
android:id="@+id/btn_right"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:layout_constraintLeft_toRightOf="@+id/btn_center"
app:layout_constraintRight_toRightOf="parent"
android:text="Destra" />
La catena viene creata automaticamente quando gli elementi hanno constraint bidirezionali. La modalità spread_inside viene impostata su qualsiasi elemento della catena tramite app:layout_constraintHorizontal_chainStyle="spread_inside". Ciò elimina la necessità di un LinearLayout con weightSum e layout_weight.
Creazione di due colonne uguali utilizzando una Guideline verticale al 50%. L'elemento sinistro è collegato al genitore sinistro e il suo bordo destro alla guideline; l'elemento destro è collegato con il suo bordo sinistro alla guideline e al genitore destro.
<androidx.constraintlayout.widget.Guideline
android:id="@+id/gl_midpoint"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="vertical"
app:layout_constraintGuide_percent="0.5" />
<TextView
android:id="@+id/left_card"
android:layout_width="0dp"
android:layout_height="0dp"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toLeftOf="@+id/gl_midpoint"
android:layout_margin="8dp"
android:background="@color/card_background" />
<TextView
android:id="@+id/right_card"
android:layout_width="0dp"
android:layout_height="0dp"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toRightOf="@+id/gl_midpoint"
app:layout_constraintRight_toRightOf="parent"
android:layout_margin="8dp"
android:background="@color/card_background" />
Una Guideline con percentuale 0.5 si adatta automaticamente alla larghezza dello schermo. Sia su tablet che su telefono, il rapporto delle colonne rimane 50/50. Per la nomenclatura sinistra/destra, utilizzare gli attributi start/end per la compatibilità RTL.
Tabella di confronto dei tre principali ViewGroup per lo sviluppo Android: ConstraintLayout, LinearLayout e RelativeLayout. Criteri: flessibilità, prestazioni, complessità del codice e casi d'uso.
| Caratteristica | ConstraintLayout | LinearLayout | RelativeLayout |
|---|---|---|---|
| Annidamento | Piatto (un livello) | Richiede annidamento per layout complessi | Un livello, ma flessibilità limitata |
| Prestazioni measure | 2 passaggi (~40% più veloce) | 4+ passaggi con weight | 2 passaggi |
| Dimensioni percentuali | Sì (guide_percent, width_percent) | Solo tramite weight/frame | No |
| Supporto RTL | Integrato (start/end) | Integrato | Tramite start/end (API 17+) |
| Barrier/Group/Flow | Sì (aiutanti virtuali) | No | No |
| Animazioni MotionLayout | Sì | No | No |
| Quando usare | Tutti i layout complessi, schermi con >5 elementi | Elenchi unidirezionali semplici, righe con pulsanti | Layout relativi semplici (codice legacy) |
Secondo Android Vitals (Google, 2025), le app che utilizzano ConstraintLayout come contenitore principale mostrano in media 18% in meno di frame jank durante il rendering di schermate complesse rispetto alle app che utilizzano LinearLayout annidati. In IT Sectr, siamo passati a ConstraintLayout come standard per tutti i layout XML nel 2018 — questo ha ridotto la profondità media della gerarchia dello schermo da 4,2 a 1,8 livelli e ha accelerato lo sviluppo di nuovi moduli del 25%.
Domande frequenti
match_parent in ConstraintLayout funziona come al solito — allunga la View fino alla dimensione del genitore. 0dp (match_constraint) significa che la dimensione della View viene calcolata dai constraint: se i constraint left e right con margini sono impostati, la larghezza = parent — marginLeft — marginRight. La differenza nel comportamento: match_parent ignora il bias e può superare i bordi durante l'animazione; match_constraint rispetta correttamente tutti i vincoli ed è raccomandato da Google come modalità principale per ConstraintLayout.
Utilizzare una combinazione di: dimensioni percentuali (layout_constraintWidth_percent) per elementi che devono occupare una parte dello schermo; Guideline con percentuali per dividere lo schermo in zone; Barrier per il posizionamento relativo a contenuti dinamici; Flow con wrapMode per l'avvolgimento delle carte in una nuova riga. Un approccio alternativo è utilizzare SlidingPaneLayout in combinazione con ConstraintLayout per interfacce master-detail su tablet.
Jetpack Compose non utilizza ConstraintLayout come ViewGroup, ma fornisce una versione Compose di ConstraintLayout (androidx.constraintlayout:constraintlayout-compose) con la stessa API in Kotlin DSL: createRefFor(), constrainAs(), linkTo(), chain(), guideFrom(). Questo è utile per layout complessi più facili da descrivere tramite constraint che con Column/Row. Tuttavia, in Compose, si consiglia di iniziare con Column/Row/Box e passare a ConstraintLayout solo quando è necessario un posizionamento relativo complesso.
In Android Studio, aprire Layout Inspector (Tools → Layout Inspector), selezionare l'app in esecuzione e passare il mouse sull'elemento problematico. Vedrai tutti i constraint, margini, padding e bias in una rappresentazione 3D. Per XML, utilizzare il pannello Design nell'editor di layout — evidenzia i conflitti di constraint in giallo e i constraint mancanti in rosso. Nel codice, assicurarsi che ogni View abbia due constraint perpendicolari, altrimenti l'elemento finirà in (0,0).
Riepilogo
Svilupperemo un'applicazione mobile chiavi in mano
IT Sectr crea applicazioni iOS e Android per startup e aziende dal 2017. Ti consulteremo e ti proporremo la soluzione migliore.
Leggi anche