Découvrons ce qu'est ConstraintLayout — un système de positionnement flexible pour Android permettant de construire des hiérarchies plates de vues avec des contraintes au lieu de LinearLayout et RelativeLayout imbriqués. ConstraintLayout résout le problème de « l'imbrication infernale » (layout nesting hell) en réduisant la profondeur de la hiérarchie à un seul niveau et en accélérant le rendu de l'écran. La bibliothèque fait partie de Jetpack et est disponible à partir d'Android 2.3 (API 9) via la support-library. Les principaux mécanismes sont décrits dans la documentation officielle d'Android.
Points clés
ConstraintLayout est un ViewGroup de la bibliothèque AndroidX ConstraintLayout, conçu pour créer des interfaces flexibles et performantes grâce à des contraintes déclaratives. Contrairement à LinearLayout qui aligne les éléments sur une ligne, ou à RelativeLayout qui positionne les éléments par rapport aux voisins, ConstraintLayout permet d'ancrer chaque élément par rapport à n'importe quel autre élément et au parent simultanément.
La bibliothèque a été annoncée à Google I/O 2016 comme une solution pour accélérer le rendu des écrans complexes. Le problème clé que résout ConstraintLayout est l'imbrication des layouts. Chaque ViewGroup imbriqué ajoute au moins deux passes de measure et une passe de layout. Un écran avec 4 niveaux d'imbrication effectue 8 passes de measure ; ConstraintLayout avec la même fonctionnalité n'effectue que 2 passes. Selon Google (Android Performance Blog, 2017), remplacer trois LinearLayouts imbriqués par un seul ConstraintLayout réduit le temps de onMeasure de 40 %.
La version actuelle ConstraintLayout 2.1.4 fonctionne stablement sur Android 2.3+ (API 9) via AndroidX. La version 2.0 a introduit le positionnement circulaire, Flow (enroulement automatique des éléments) et la prise en charge de MotionLayout. ConstraintLayout est essentiel pour comprendre le développement Android moderne — il est utilisé dans Jetpack Compose comme concept de base des modificateurs, dans les modèles par défaut d'Android Studio et dans Material Design 3.
La hiérarchie plate de ConstraintLayout signifie que toutes les vues enfants sont au même niveau d'imbrication. Au lieu de placer l'élément A dans un LinearLayout, et le LinearLayout dans un RelativeLayout, tous les éléments sont liés directement au ConstraintLayout parent ou les uns aux autres via des attributs. Cela offre : une consommation mémoire réduite (chaque ViewGroup est un objet dans le heap Java), une passe de layout plus rapide (moins d'appels récursifs) et un comportement plus prévisible lors des changements de taille d'écran.
Une contrainte est une connexion entre le bord d'une vue (ou son centre) et le bord d'une autre vue ou du parent. Chaque vue peut avoir jusqu'à 8 contraintes : left, top, right, bottom, start, end, baseline et center. Au minimum, deux contraintes perpendiculaires suffisent pour le positionnement (par exemple, top + left).
Format de l'attribut : app:layout_constraint[Source]_to[Target]Of="[id]" — où Source est le bord lié (Left, Right, Top, Bottom, Start, End, Baseline) et Target est le bord cible. Exemple : app:layout_constraintTop_toBottomOf="@+id/header" signifie « le bord supérieur de l'élément actuel est lié au bord inférieur de l'élément header ». Pour lier au parent, l'id parent est utilisé.
Le bias (biais) est un paramètre qui fonctionne lorsque des contraintes opposées sont présentes (left + right ou top + bottom). Les valeurs vont de 0 à 1 : 0 — plaqué contre le bord gauche/supérieur, 0.5 — centré, 1 — contre le bord droit/inférieur. Attributs : layout_constraintHorizontal_bias (0.0–1.0) et layout_constraintVertical_bias. Les marges sont définies avec les standards android:layout_margin*, mais les contraintes et les marges fonctionnent indépendamment : la marge est un décalage par rapport à la contrainte, et non par rapport à la vue voisine.
À partir de ConstraintLayout 1.1+, la prise en charge des tailles en pourcentage a été ajoutée via layout_constraintWidth_percent et layout_constraintHeight_percent. Une valeur de 0.3 signifie 30 % de la largeur/hauteur du parent. Combiné avec le bias, cela permet de créer des mises en page adaptatives sans programmation.
Une chaîne (Chain) est un groupe de deux vues ou plus connectées par des contraintes bidirectionnelles (A est lié à B, B est lié à A). Les chaînes distribuent automatiquement l'espace entre les éléments selon l'un des modes : spread (uniformément, en tenant compte des marges), spread_inside (uniformément, éléments extérieurs sans marge de bord), packed (éléments serrés avec un bias commun). Le mode est défini via l'attribut app:layout_constraintHorizontal_chainStyle ou layout_constraintVertical_chainStyle.
Une Guideline (guide) est une vue auxiliaire, invisible à l'exécution, qui définit une ligne pour la liaison. Une Guideline peut être horizontale ou verticale, positionnée en dp, en pourcentages (app:layout_constraintGuide_percent) ou avec un décalage par rapport au bord (app:layout_constraintGuide_begin/end). Les Guidelines sont indispensables pour les mises en page adaptatives — par exemple, pour diviser l'écran en deux moitiés égales quelle que soit la taille de l'appareil.
Selon Google I/O 2017, les chaînes avec spread_inside sont 15 à 20 % plus performantes que les LinearLayouts imbriqués avec weight, car elles évitent la double passe de measure nécessaire au calcul du weight.
Une Barrier (barrière) est une vue virtuelle qui ajuste dynamiquement sa position en fonction de la taille d'un groupe d'éléments. Contrairement à une Guideline à position fixe, une Barrier est « poussée » par l'élément le plus large du groupe. Par exemple, si vous avez un titre et une description de longueurs inconnues, une Barrier liée au bord droit du texte le plus large permet de placer une icône juste après. Attributs : app:barrierDirection (left, right, top, bottom, start, end) et app:constraint_referenced_ids (liste d'ids séparés par des virgules).
Group est un conteneur virtuel qui gère la visibilité de plusieurs vues simultanément. Au lieu d'appeler setVisibility pour chaque élément individuellement, il suffit de modifier la visibilité d'un seul Group. Group n'affecte pas le positionnement — seulement la visibilité. Flow est un assistant virtuel pour créer des mises en page « fluides » : les éléments passent automatiquement à une nouvelle ligne/colonne lorsque l'espace manque, comme le texte dans un paragraphe. Flow prend en charge wrapMode : none, chain et aligned.
Ces outils (Barrier, Group, Flow, Guideline) sont appelés assistants virtuels car ils ne sont pas des vues au sens classique — ils ne prennent pas de place dans la hiérarchie et ne participent pas aux événements de focus ou tactiles. Leur objectif est de simplifier la maintenance de mises en page complexes sans ajouter de conteneurs imbriqués.
Un formulaire de connexion simple avec un champ email, un champ mot de passe et un bouton. Tous les éléments sont liés au parent, sauf le bouton — il se trouve sous le champ mot de passe. Une hiérarchie plate est utilisée — les trois éléments sont au même niveau.
<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="Se connecter" />
</androidx.constraintlayout.widget.ConstraintLayout>
Tous les éléments ont une largeur de 0dp (match_constraint), ce qui signifie qu'ils s'étendent de la contrainte start à end en tenant compte des marges horizontales. Cela équivaut à match_parent avec marges, mais sans imbrication.
Trois boutons répartis uniformément horizontalement avec des marges de bord. La chaîne spread_inside place les boutons extérieurs aux bords et celui du milieu centré entre eux.
<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="Gauche" />
<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="Centre" />
<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="Droite" />
La chaîne est créée automatiquement lorsque les éléments ont des contraintes bidirectionnelles. Le mode spread_inside est défini sur n'importe quel élément de la chaîne via app:layout_constraintHorizontal_chainStyle="spread_inside". Cela élimine le besoin d'un LinearLayout avec weightSum et layout_weight.
Création de deux colonnes égales à l'aide d'une Guideline verticale à 50 %. L'élément gauche est lié au parent gauche et son bord droit à la guideline ; l'élément droit est lié par son bord gauche à la guideline et au parent droit.
<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" />
Une Guideline avec un pourcentage de 0.5 s'adapte automatiquement à la largeur de l'écran. Sur tablette comme sur téléphone, le ratio des colonnes reste 50/50. Pour la nomenclature gauche/droite, utilisez les attributs start/end pour la compatibilité RTL.
Tableau comparatif des trois principaux ViewGroups pour le développement Android : ConstraintLayout, LinearLayout et RelativeLayout. Critères : flexibilité, performance, complexité du code et cas d'utilisation.
| Caractéristique | ConstraintLayout | LinearLayout | RelativeLayout |
|---|---|---|---|
| Imbrication | Plate (un niveau) | Nécessite une imbrication pour les layouts complexes | Un niveau, mais flexibilité limitée |
| Performance de measure | 2 passes (~40 % plus rapide) | 4+ passes avec weight | 2 passes |
| Tailles en pourcentage | Oui (guide_percent, width_percent) | Uniquement via weight/frame | Non |
| Support RTL | Intégré (start/end) | Intégré | Via start/end (API 17+) |
| Barrier/Group/Flow | Oui (assistants virtuels) | Non | Non |
| Animations MotionLayout | Oui | Non | Non |
| Quand utiliser | Tous les layouts complexes, écrans avec >5 éléments | Listes unidirectionnelles simples, lignes avec boutons | Layouts relatifs simples (code legacy) |
Selon Android Vitals (Google, 2025), les applications utilisant ConstraintLayout comme conteneur principal présentent en moyenne 18 % de trames jank en moins lors du rendu d'écrans complexes par rapport aux applications utilisant des LinearLayouts imbriqués. Chez IT Sectr, nous sommes passés à ConstraintLayout comme standard pour tous les layouts XML en 2018 — cela a réduit la profondeur moyenne de la hiérarchie d'écran de 4,2 à 1,8 niveaux et accéléré le développement de nouveaux formulaires de 25 %.
Foire aux questions
match_parent dans ConstraintLayout fonctionne comme d'habitude — il étire la vue à la taille du parent. 0dp (match_constraint) signifie que la taille de la vue est calculée à partir des contraintes : si les contraintes left et right avec marges sont définies, la largeur = parent — marginLeft — marginRight. La différence de comportement : match_parent ignore le bias et peut déborder pendant l'animation ; match_constraint respecte correctement toutes les contraintes et est recommandé par Google comme mode principal pour ConstraintLayout.
Utilisez une combinaison de : tailles en pourcentage (layout_constraintWidth_percent) pour les éléments qui doivent occuper une partie de l'écran ; Guideline avec pourcentages pour diviser l'écran en zones ; Barrier pour le positionnement relatif au contenu dynamique ; Flow avec wrapMode pour le retour à la ligne des cartes. Une approche alternative consiste à utiliser SlidingPaneLayout en combinaison avec ConstraintLayout pour les interfaces master-detail sur tablettes.
Jetpack Compose n'utilise pas ConstraintLayout en tant que ViewGroup, mais fournit une version Compose de ConstraintLayout (androidx.constraintlayout:constraintlayout-compose) avec la même API en Kotlin DSL : createRefFor(), constrainAs(), linkTo(), chain(), guideFrom(). C'est utile pour les layouts complexes plus faciles à décrire via des contraintes qu'avec Column/Row. Cependant, dans Compose, il est recommandé de commencer par Column/Row/Box et de passer à ConstraintLayout uniquement lorsque des positionnements relatifs complexes sont nécessaires.
Dans Android Studio, ouvrez Layout Inspector (Tools → Layout Inspector), sélectionnez l'application en cours d'exécution et survolez l'élément problématique. Vous verrez toutes les contraintes, marges, padding et bias dans une représentation 3D. Pour XML, utilisez le panneau Design dans l'éditeur de layout — il surligne les conflits de contraintes en jaune et les contraintes manquantes en rouge. Dans le code, assurez-vous que chaque vue a deux contraintes perpendiculaires, sinon l'élément se retrouvera à (0,0).
Résumé
Nous développerons une application mobile clé en main
IT Sectr crée des applications iOS et Android pour les startups et les entreprises depuis 2017. Nous vous conseillerons et vous proposerons la meilleure solution.
Lisez aussi