Vysvětlujeme, co je ConstraintLayout — flexibilní systém pozicování pro Android, který umožňuje vytvářet ploché hierarchie zobrazení pomocí omezení (constraints) namísto vnořených LinearLayout a RelativeLayout. ConstraintLayout řeší problém "zapleteného vnořování" (layout nesting hell), snižuje hloubku hierarchie na jednu úroveň a urychluje vykreslování obrazovky. Knihovna je součástí Jetpack a je dostupná od Android 2.3 (API 9) přes support-library. Základní mechanismy jsou popsány v oficiální dokumentaci Android.
Hlavní body
ConstraintLayout — je ViewGroup z knihovny AndroidX ConstraintLayout, určený pro vytváření flexibilních a výkonných rozhraní prostřednictvím deklarativních omezení. Na rozdíl od LinearLayout, které řadí prvky do jedné řady, nebo RelativeLayout, které pozicuje relativně k sousedům, ConstraintLayout umožňuje připojit každý prvek relativně k libovolným jiným prvkům a rodiči současně.
Knihovna byla oznámena na Google I/O 2016 jako řešení pro urychlení vykreslování složitých obrazovek. Klíčovým problémem, který ConstraintLayout řeší, je vnořování rozvržení. Každý vnořený ViewGroup přidá nejméně dva průchody measure a jeden průchod layout. Obrazovka se 4 úrovněmi vnoření provádí 8 průchodů measure; ConstraintLayout se stejnou funkcionalitou — pouze 2 průchody. Podle údajů Google (Android Performance Blog, 2017) nahrazení tří vnořených LinearLayout jedním ConstraintLayout snižuje čas onMeasure o 40%.
Aktuální verze ConstraintLayout 2.1.4 stabilně funguje na Android 2.3+ (API 9) přes AndroidX. Ve verzi 2.0 se objevilo kruhové pozicování, Flow (automatický přesun prvků) a podpora MotionLayout. ConstraintLayout je nezbytný pro pochopení moderního vývoje Android — používá se v Jetpack Compose jako základní koncept modifikátorů, ve výchozích šablonách Android Studio a v Material Design 3.
Plochá hierarchie ConstraintLayout znamená, že všechna podřízená zobrazení jsou na stejné úrovni vnoření. Místo umístění prvku A do LinearLayout a LinearLayout do RelativeLayout jsou všechny prvky připojeny přímo k nadřazenému ConstraintLayout nebo k sobě navzájem prostřednictvím atributů. To přináší: nižší spotřebu paměti (každý ViewGroup je objekt v Java heap), zrychlení průchodu layout (méně rekurzivních volání), předvídatelnější chování při změně velikosti obrazovky.
Omezení — je spojení mezi okrajem jednoho zobrazení (nebo jeho středem) a okrajem jiného zobrazení nebo rodiče. Každé zobrazení může mít až 8 omezení: levé, horní, pravé, dolní, start, end, baseline a střed. Pro pozicování stačí minimálně dvě kolmá omezení (např. horní + levé).
Formát atributu: app:layout_constraint[Zdroj]_to[Cíl]Of="[id]" — kde Zdroj je připojovaný okraj (Left, Right, Top, Bottom, Start, End, Baseline), Cíl je okraj cíle. Příklad: app:layout_constraintTop_toBottomOf="@+id/header" znamená "horní hranice aktuálního prvku je připojena k dolní hranici prvku header". Pro připojení k rodiči se používá id parent.
Bias (posun) — parametr fungující při protilehlých omezeních (left + right nebo top + bottom). Hodnota od 0 do 1: 0 — přitisknuto k levému/hornímu okraji, 0.5 — na středu, 1 — k pravému/dolnímu okraji. Atributy: layout_constraintHorizontal_bias (0.0–1.0) a layout_constraintVertical_bias. Okraj se nastavuje standardními android:layout_margin*, ale omezení a okraj fungují nezávisle: okraj je vzdálenost od omezení, ne od sousedního zobrazení.
Od ConstraintLayout 1.1+ je dostupná podpora procentuálních velikostí prostřednictvím layout_constraintWidth_percent a layout_constraintHeight_percent. Hodnota 0.3 znamená 30% šířky/výšky rodiče. V kombinaci s bias to umožňuje vytvářet adaptivní rozvržení bez programování.
Chain (řetězec) — je skupina dvou nebo více zobrazení spojených obousměrnými omezeními (A připojeno k B, B k A). Řetězce automaticky rozdělují prostor mezi prvky v jednom z režimů: spread (rovnoměrně, s ohledem na okraje), spread_inside (rovnoměrně, krajní prvky bez mezery od okrajů), packed (prvky přitisknuté k sobě se společným bias). Režim se nastavuje atributem app:layout_constraintHorizontal_chainStyle nebo layout_constraintVertical_chainStyle.
Guideline (vodítko) — pomocné zobrazení, neviditelné za běhu, ale určující čáru pro připojení. Guideline může být vodorovný nebo svislý, umístěný v dp, procentech (app:layout_constraintGuide_percent) nebo s odstupem od okraje (app:layout_constraintGuide_begin/end). Vodítka jsou nepostradatelná pro adaptivní rozvržení — například pro rozdělení obrazovky na dvě stejné poloviny bez ohledu na velikost zařízení.
Podle údajů Google I/O 2017 jsou řetězce s spread_inside o 15–20% výkonnější než vnořené LinearLayout s weight, protože se vyhýbají dvojitému průchodu measure potřebnému pro výpočet weight.
Barrier (bariéra) — virtuální zobrazení, které dynamicky přizpůsobuje svou pozici velikosti skupiny prvků. Na rozdíl od Guideline s pevnou pozicí je Barrier "tlačen" nejširším prvkem skupiny. Například pokud máte nadpis a popis neznámé délky, Barrier připojený k pravému okraji nejširšího textu umožňuje umístit ikonu hned za ně. Atributy: app:barrierDirection (left, right, top, bottom, start, end) a app:constraint_referenced_ids (seznam id oddělených čárkou).
Group — virtuální kontejner spravující viditelnost několika zobrazení současně. Místo volání setVisibility pro každý prvek zvlášť stačí změnit viditelnost jednoho Group. Group neovlivňuje pozicování — pouze viditelnost. Flow — virtuální pomocník pro vytváření "tekutých" rozvržení: prvky se automaticky přesouvají na nový řádek/sloupec při nedostatku místa, jako text v odstavci. Flow podporuje wrapMode: none, chain a aligned.
Tyto nástroje (Barrier, Group, Flow, Guideline) se nazývají virtuální pomocníci, protože nejsou zobrazeními v klasickém smyslu — nezabírají místo v hierarchii a neúčastní se fokusu nebo dotykových událostí. Jejich cílem je zjednodušit údržbu složitých rozvržení bez přidávání vnořených kontejnerů.
Jednoduchý přihlašovací formulář s polem email, heslem a tlačítkem. Všechny prvky jsou připojeny k parent, kromě tlačítka — to je pod polem hesla. Používá plochou hierarchii — všechny tři prvky na stejné úrovni.
<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="Přihlásit" />
</androidx.constraintlayout.widget.ConstraintLayout>
Všechny prvky mají šířku 0dp (match_constraint), tedy se táhnou od start do end omezení s ohledem na vodorovný okraj. To je obdoba match_parent s mezerami, ale bez vnoření.
Tři tlačítka, rovnoměrně rozmístěná vodorovně s mezerami od okrajů. Řetězec spread_inside umísťuje krajní tlačítka k okrajům a prostřední tlačítko doprostřed mezi ně.
<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="Vlevo" />
<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="Střed" />
<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="Vpravo" />
Řetězec se vytváří automaticky, když mají prvky obousměrná omezení. Režim spread_inside se nastavuje na libovolném prvku řetězce pomocí app:layout_constraintHorizontal_chainStyle="spread_inside". To eliminuje potřebu používat LinearLayout s weightSum a layout_weight.
Vytvoření dvou stejných sloupců pomocí svislého Guideline na 50%. Levý prvek je připojen k levému okraji parent a pravým okrajem ke guideline; pravý prvek — levým okrajem ke guideline a k pravému okraji parent.
<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" />
Guideline s procentem 0.5 se automaticky přizpůsobuje šířce obrazovky. Na tabletu a telefonu zůstanou poměry sloupců 50/50. Pro levou/pravou nomenklaturu používejte atributy start/end pro kompatibilitu RTL.
Srovnávací tabulka tří hlavních ViewGroup pro vývoj Android: ConstraintLayout, LinearLayout a RelativeLayout. Kritéria: flexibilita, výkon, složitost kódu a oblasti použití.
| Vlastnost | ConstraintLayout | LinearLayout | RelativeLayout |
|---|---|---|---|
| Vnoření | Ploché (jedna úroveň) | Vyžaduje vnoření pro složitá rozvržení | Jedna úroveň, ale omezená flexibilita |
| Výkon measure | 2 průchody (~40% rychlejší) | 4+ průchody při weight | 2 průchody |
| Procentuální velikosti | Ano (guide_percent, width_percent) | Pouze přes weight/frame | Ne |
| Podpora RTL | Vestavěná (start/end) | Vestavěná | Přes start/end (API 17+) |
| Barrier/Group/Flow | Ano (virtuální pomocníci) | Ne | Ne |
| MotionLayout animace | Ano | Ne | Ne |
| Kdy použít | Všechna složitá rozvržení, obrazovky s >5 prvky | Jednoduché jednosměrné seznamy, řady tlačítek | Jednoduchá relativní rozvržení (z legacy kódu) |
Podle Android Vitals (Google, 2025) vykazují aplikace používající ConstraintLayout jako hlavní kontejner v průměru o 18% méně jank snímků při vykreslování složitých obrazovek ve srovnání s aplikacemi na vnořených LinearLayout. V IT Sectr jsme přešli na ConstraintLayout jako standard pro všechna XML rozvržení v roce 2018 — to snížilo průměrnou hloubku hierarchie obrazovek ze 4,2 na 1,8 úrovně a urychlilo vývoj nových formulářů o 25%.
Často kladené otázky
match_parent v ConstraintLayout funguje jako obvykle — roztahuje zobrazení na velikost rodiče. 0dp (match_constraint) znamená, že velikost zobrazení se vypočítá z omezení: pokud jsou nastavena levé a pravé omezení s okrajem, šířka = parent — marginLeft — marginRight. Rozdíl v chování: match_parent ignoruje bias a může při animaci překročit hranice; match_constraint správně zohledňuje všechna omezení a Google jej doporučuje jako hlavní režim pro ConstraintLayout.
Použijte kombinaci: procentuální velikosti (layout_constraintWidth_percent) pro prvky, které mají zabírat část obrazovky; Guideline s procenty pro rozdělení obrazovky na zóny; Barrier pro pozicování vůči dynamickému obsahu; Flow s wrapMode pro přesun karet na nový řádek. Alternativní přístup — použití SlidingPaneLayout v kombinaci s ConstraintLayout pro master-detail rozhraní na tabletech.
Jetpack Compose nepoužívá ConstraintLayout jako ViewGroup, ale poskytuje Compose verzi ConstraintLayout (androidx.constraintlayout:constraintlayout-compose) se stejným API v Kotlin DSL: createRefFor(), constrainAs(), linkTo(), chain(), guideFrom(). To je užitečné pro složitá rozvržení, která se snáze popisují pomocí omezení než pomocí Column/Row. V Compose se však doporučuje začínat s Column/Row/Box a přejít na ConstraintLayout pouze tehdy, když jsou potřeba složitá relativní pozicování.
V Android Studio otevřete Layout Inspector (Tools → Layout Inspector), vyberte běžící aplikaci a najeďte na problematický prvek. Uvidíte všechna omezení, okraje, padding a bias v trojrozměrném zobrazení. Pro XML použijte panel Design v editoru rozvržení — zvýrazňuje konflikty omezení žlutě a chybějící omezení červeně. V kódu zkontrolujte, že každé zobrazení má dvě kolmá omezení, jinak prvek skončí na (0,0).
Shrnutí
Vyvineme mobilní aplikaci na klíč
IT Sectr vytváří aplikace pro iOS a Android pro startupy a podniky od roku 2017. Poradíme vám a navrhneme nejlepší řešení.
Přečtěte si také