Vi förklarar vad ConstraintLayout är — ett flexibelt positioneringssystem för Android som gör det möjligt att bygga platta vyhierarkier med hjälp av begränsningar (constraints) istället för nästlade LinearLayout och RelativeLayout. ConstraintLayout löser problemet med "layout nesting hell", minskar hierarkidjupet till en nivå och påskyndar renderingen av skärmen. Biblioteket ingår i Jetpack och är tillgängligt från Android 2.3 (API 9) via support-library. De grundläggande mekanismerna beskrivs i officiella Android-dokumentationen.
Huvudpunkter
ConstraintLayout — är en ViewGroup från AndroidX ConstraintLayout-biblioteket, utformad för att skapa flexibla och effektiva gränssnitt genom deklarativa begränsningar. Till skillnad från LinearLayout, som arrangerar element i en rad, eller RelativeLayout, som positionerar relativt till grannar, låter ConstraintLayout dig binda varje element relativt till andra element och föräldern samtidigt.
Biblioteket tillkännagavs på Google I/O 2016 som en lösning för att påskynda rendering av komplexa skärmar. Nyckelproblemet som ConstraintLayout löser är nästling av layouter. Varje nästlad ViewGroup lägger till minst två measure-pass och ett layout-pass. En skärm med 4 nivåer av nästling utför 8 measure-pass; ConstraintLayout med samma funktionalitet — endast 2 pass. Enligt Google (Android Performance Blog, 2017) minskar byte av tre nästlade LinearLayout mot ett ConstraintLayout onMeasure-tiden med 40%.
Den aktuella versionen ConstraintLayout 2.1.4 fungerar stabilt på Android 2.3+ (API 9) via AndroidX. I version 2.0 tillkom cirkulär positionering, Flow (automatisk överföring av element) och MotionLayout-stöd. ConstraintLayout är obligatoriskt för att förstå modern Android-utveckling — det används i Jetpack Compose som grundkoncept för modifierare, i Android Studios standardsabloner och i Material Design 3.
Platt hierarki ConstraintLayout innebär att alla underordnade vyer är på samma nästlingsnivå. Istället för att placera element A i en LinearLayout och LinearLayout i en RelativeLayout, binds alla element direkt till förälder-ConstraintLayout eller till varandra via attribut. Detta ger: lägre minnesförbrukning (varje ViewGroup är ett objekt i Java heap), snabbare layout-pass (färre rekursiva anrop), mer förutsägbart beteende vid ändring av skärmstorlek.
Begränsning — är en förbindelse mellan kanten på en vy (eller dess mitt) och kanten på en annan vy eller föräldern. Varje vy kan ha upp till 8 begränsningar: vänster, övre, höger, nedre, start, end, baseline och mitt. För positionering räcker det med minst två vinkelräta begränsningar (t.ex. övre + vänster).
Attributformat: app:layout_constraint[Källa]_to[Mål]Of="[id]" — där Källa är den bundna kanten (Left, Right, Top, Bottom, Start, End, Baseline), Mål är målets kant. Exempel: app:layout_constraintTop_toBottomOf="@+id/header" betyder "den övre gränsen för det aktuella elementet är bunden till den nedre gränsen för elementet header". För bindning till föräldern används id parent.
Bias (förskjutning) — parameter som fungerar vid motsatta begränsningar (left + right eller top + bottom). Värde från 0 till 1: 0 — tryckt mot vänster/övre kant, 0.5 — centrerad, 1 — mot höger/nedre kant. Attribut: layout_constraintHorizontal_bias (0.0–1.0) och layout_constraintVertical_bias. Marginal ställs in med standard android:layout_margin*, men begränsning och marginal fungerar oberoende: marginal är avståndet från begränsningen, inte från den angränsande vyn.
Från ConstraintLayout 1.1+ är stöd för procentuella storlekar tillgängligt via layout_constraintWidth_percent och layout_constraintHeight_percent. Värdet 0.3 betyder 30% av förälderns bredd/höjd. I kombination med bias gör detta det möjligt att skapa adaptiva layouter utan programmering.
Chain (kedja) — är en grupp av två eller flera vyer kopplade genom dubbelriktade begränsningar (A bunden till B, B till A). Kedjor fördelar automatiskt utrymmet mellan element i ett av lägena: spread (jämnt, med hänsyn till marginaler), spread_inside (jämnt, ytterelement utan mellanrum från kanter), packed (element tryckta mot varandra med gemensam bias). Läget ställs in med attributet app:layout_constraintHorizontal_chainStyle eller layout_constraintVertical_chainStyle.
Guideline (riktlinje) — en hjälpvy, osynlig vid körning, men som anger en linje för bindning. Guideline kan vara horisontell eller vertikal, positionerad i dp, procent (app:layout_constraintGuide_percent) eller med avstånd från kanten (app:layout_constraintGuide_begin/end). Riktlinjer är oumbärliga för adaptiva layouter — till exempel för att dela skärmen i två lika halvor oavsett enhetens storlek.
Enligt Google I/O 2017 är kedjor med spread_inside 15–20% effektivare än nästlade LinearLayout med weight, eftersom de undviker det dubbla measure-pass som krävs för att beräkna weight.
Barrier (barriär) — en virtuell vy som dynamiskt justerar sin position efter storleken på en grupp element. Till skillnad från Guideline med fast position blir Barrier "knuffad" av det bredaste elementet i gruppen. Om du till exempel har en rubrik och beskrivning med okänd längd, låter en Barrier bunden till högerkanten av den bredaste texten dig placera en ikon direkt efter dem. Attribut: app:barrierDirection (left, right, top, bottom, start, end) och app:constraint_referenced_ids (kommaseparerad lista med id).
Group — en virtuell behållare som hanterar synligheten för flera vyer samtidigt. Istället för att anropa setVisibility för varje element separat, räcker det att ändra synligheten för en Group. Group påverkar inte positionering — bara synlighet. Flow — en virtuell hjälpare för att skapa "flytande" layouter: element överförs automatiskt till en ny rad/kolumn vid utrymmesbrist, som text i ett stycke. Flow stöder wrapMode: none, chain och aligned.
Dessa verktyg (Barrier, Group, Flow, Guideline) kallas virtuella hjälpare eftersom de inte är vyer i klassisk mening — de tar inte plats i hierarkin och deltar inte i fokus eller tryckhändelser. Deras mål är att förenkla underhållet av komplexa layouter utan att lägga till nästlade behållare.
Ett enkelt inloggningsformulär med e-postfält, lösenord och knapp. Alla element är bundna till parent, förutom knappen — den ligger under lösenordsfältet. Använder platt hierarki — alla tre element på samma nivå.
<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="Logga in" />
</androidx.constraintlayout.widget.ConstraintLayout>
Alla element har bredden 0dp (match_constraint), det vill säga de sträcker sig från start till end av begränsningen med hänsyn till horisontell marginal. Detta är en motsvarighet till match_parent med mellanrum, men utan nästling.
Tre knappar, jämnt fördelade horisontellt med mellanrum från kanter. Spread_inside-kedjan placerar ytterknapparna vid kanterna och mittknappen centrerad mellan dem.
<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="Vänster" />
<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="Mitten" />
<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="Höger" />
Kedjan skapas automatiskt när element har dubbelriktade begränsningar. Spread_inside-läget ställs in på valfritt element i kedjan via app:layout_constraintHorizontal_chainStyle="spread_inside". Detta eliminerar behovet av att använda LinearLayout med weightSum och layout_weight.
Skapa två lika kolumner via en vertikal Guideline på 50%. Det vänstra elementet är bundet till vänsterkanten av parent och med högerkanten till guideline; det högra elementet — med vänsterkanten till guideline och till högerkanten av 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 med procent 0.5 anpassar sig automatiskt till skärmbredden. På surfplatta och telefon förblir kolumnförhållandena 50/50. För vänster/höger-nomenklatur, använd start/end-attribut för RTL-kompatibilitet.
Jämförelsetabell för tre huvudsakliga ViewGroup för Android-utveckling: ConstraintLayout, LinearLayout och RelativeLayout. Kriterier: flexibilitet, prestanda, kodkomplexitet och tillämpningsområden.
| Egenskap | ConstraintLayout | LinearLayout | RelativeLayout |
|---|---|---|---|
| Nästling | Platt (en nivå) | Kräver nästling för komplexa layouter | En nivå, men begränsad flexibilitet |
| Measure-prestanda | 2 pass (~40% snabbare) | 4+ pass vid weight | 2 pass |
| Procentuella storlekar | Ja (guide_percent, width_percent) | Endast via weight/frame | Nej |
| RTL-stöd | Inbyggt (start/end) | Inbyggt | Via start/end (API 17+) |
| Barrier/Group/Flow | Ja (virtuella hjälpare) | Nej | Nej |
| MotionLayout-animationer | Ja | Nej | Nej |
| När att använda | Alla komplexa layouter, skärmar med >5 element | Enkla enkelriktade listor, knapprader | Enkla relativa layouter (från äldre kod) |
Enligt Android Vitals (Google, 2025) visar appar som använder ConstraintLayout som primär behållare i genomsnitt 18% färre jank-bildrutor vid rendering av komplexa skärmar jämfört med appar med nästlade LinearLayout. På IT Sectr bytte vi till ConstraintLayout som standard för alla XML-layouter 2018 — detta minskade det genomsnittliga hierarkidjupet för skärmar från 4,2 till 1,8 nivå och påskyndade utvecklingen av nya formulär med 25%.
Vanliga frågor
match_parent fungerar i ConstraintLayout som vanligt — sträcker ut vyn till förälderns storlek. 0dp (match_constraint) innebär att vyns storlek beräknas från begränsningarna: om vänster och höger begränsningar med marginal är inställda, bredd = parent — marginLeft — marginRight. Skillnad i beteende: match_parent ignorerar bias och kan överskrida gränser under animation; match_constraint tar korrekt hänsyn till alla begränsningar och rekommenderas av Google som primärt läge för ConstraintLayout.
Använd en kombination av: procentuella storlekar (layout_constraintWidth_percent) för element som ska uppta en del av skärmen; Guideline med procent för att dela skärmen i zoner; Barrier för positionering relativt dynamiskt innehåll; Flow med wrapMode för att överföra kort till en ny rad. Ett alternativt tillvägagångssätt — använd SlidingPaneLayout i kombination med ConstraintLayout för master-detail-gränssnitt på surfplattor.
Jetpack Compose använder inte ConstraintLayout som ViewGroup, men tillhandahåller Compose-versionen av ConstraintLayout (androidx.constraintlayout:constraintlayout-compose) med samma API i Kotlin DSL: createRefFor(), constrainAs(), linkTo(), chain(), guideFrom(). Detta är användbart för komplexa layouter som är lättare att beskriva genom begränsningar än genom Column/Row. I Compose rekommenderas dock att börja med Column/Row/Box och bara gå över till ConstraintLayout när komplex relativ positionering behövs.
I Android Studio öppnar du Layout Inspector (Tools → Layout Inspector), välj den körbara appen och hovra över det problematiska elementet. Du ser alla begränsningar, marginaler, padding och bias i en tredimensionell vy. För XML, använd Design-panelen i layoutredigeraren — den markerar begränsningskonflikter med gult och avsaknad av begränsningar med rött. I koden kontrollera att varje vy har två vinkelräta begränsningar, annars hamnar elementet på (0,0).
Sammanfattning
Vi utvecklar en mobil applikation nyckelfärdigt
IT Sectr skapar iOS- och Android-applikationer för startups och företag sedan 2017. Vi ger dig råd och föreslår den bästa lösningen.
Läs också