Објашњавамо шта је ConstraintLayout — флексибилан систем позиционирања за Android, који омогућава изградњу равних хијерархија приказа помоћу констрејната (ограничења) уместо угнежђених LinearLayout и RelativeLayout. ConstraintLayout решава проблем „пакленог угнежђивања" (layout nesting hell), смањујући дубину хијерархије на један ниво и убрзавајући приказ екрана. Библиотека је део Jetpack-а и доступна је од Android 2.3 (API 9) преко support-library. Основни механизми описани су у званичној документацији Android-а.
Главно
ConstraintLayout — је ViewGroup из библиотеке AndroidX ConstraintLayout, намењена за креирање флексибилних и ефикасних интерфејса путем декларативних ограничења (constraints). За разлику од LinearLayout-а, који поређа елементе у једну линију, или RelativeLayout-а, који позиционира у односу на суседе, ConstraintLayout омогућава да се сваки елемент веже истовремено у односу на било које друге елементе и родитеља.
Библиотека је најављена на Google I/O 2016 као решење за убрзавање приказивања сложених екрана. Кључни проблем који ConstraintLayout решава је угнежђивање изгледа. Сваки угнежђени ViewGroup додаје најмање два пролаза measure и један пролаз layout. Екран са 4 нивоа угнежђивања извршава 8 пролаза measure; ConstraintLayout са истом функционалношћу — само 2 пролаза. Према подацима Google-а (Android Performance Blog, 2017), замена три угнежђена LinearLayout-а једним ConstraintLayout-ом смањује време onMeasure за 40%.
Тренутна верзија ConstraintLayout 2.1.4 стабилно ради на Android 2.3+ (API 9) преко AndroidX-а. У верзији 2.0 појавило се кружно позиционирање, Flow (аутоматски пренос елемената) и подршка за MotionLayout. ConstraintLayout је обавезан за разумевање савременог Android развоја — користи се у Jetpack Compose-у као основна концепција модификатора, у подразумеваним шаблонима Android Studio-а и у Material Design 3.
Равна хијерархија ConstraintLayout-а значи да се сва дечја View налазе на истом нивоу угнежђивања. Уместо да се елемент А смести у LinearLayout, а LinearLayout у RelativeLayout, сви елементи се директно везују за родитељски ConstraintLayout или један за другог преко атрибута. Ово даје: мању потрошњу меморије (сваки ViewGroup је објекат у Java heap-у), убрзање layout passa (мање рекурзивних позива), предвидљивије понашање при промени величине екрана.
Констрејнт — је веза између ивице једног View-а (или његовог центра) и ивице другог View-а или родитеља. Сваки View може имати до 8 констрејната: леви, горњи, десни, доњи, start, end, baseline и центар. Минимално за позиционирање су довољна два окомита констрејнта (нпр. горњи + леви).
Формат атрибута: app:layout_constraint[Извор]_to[Циљ]Of="[id]" — где је Извор везана ивица (Left, Right, Top, Bottom, Start, End, Baseline), Циљ је ивица циља. Пример: app:layout_constraintTop_toBottomOf="@+id/header" значи „горња граница тренутног елемента је везана за доњу границу елемента header". За везивање за родитеља користи се id parent.
Bias (померај) — параметар који ради при постојању супротних констрејната (left + right или top + bottom). Вредност од 0 до 1: 0 — притиснут уз леву/горњу ивицу, 0.5 — центриран, 1 — уз десну/доњу ивицу. Атрибути: layout_constraintHorizontal_bias (0.0–1.0) и layout_constraintVertical_bias. Маргина се задаје стандардним android:layout_margin*, али констрејнт и маргина раде независно: маргина је растојање од констрејнта, а не од суседног View-а.
Од ConstraintLayout 1.1+ доступна је подршка за процентуалне величине преко layout_constraintWidth_percent и layout_constraintHeight_percent. Вредност 0.3 значи 30% ширине/висине родитеља. У комбинацији са bias-ом, ово омогућава креирање адаптивних изгледа без програмирања.
Chain (ланац) — је група од два или више View-а повезаних двосмерним констрејнтима (A везан за B, B за A). Ланци аутоматски распоређују простор између елемената у једном од режима: spread (равномерно, са уважавањем маргина), spread_inside (равномерно, ивични елементи без размака од ивица), packed (елементи притиснути један уз другог са заједничким bias-ом). Режим се задаје атрибутом app:layout_constraintHorizontal_chainStyle или layout_constraintVertical_chainStyle.
Guideline (водич) — помоћни View, невидљив у рантајму, али задаје линију за везивање. Guideline може бити хоризонталан или вертикалан, позициониран у dp, процентима (app:layout_constraintGuide_percent) или са размаком од ивице (app:layout_constraintGuide_begin/end). Guidelines су незаменљиви за адаптивне изгледе — на пример, за поделу екрана на две једнаке половине без обзира на величину уређаја.
Према подацима Google I/O 2017, ланци са spread_inside су 15–20% ефикаснији од угнежђених LinearLayout-а са weight, јер избегавају двоструки пролаз measure неопходан за израчунавање weight-а.
Barrier (баријера) — виртуелни View који динамички прилагођава своју позицију величини групе елемената. За разлику од Guideline-а са фиксном позицијом, Barrier се „гура" од најширег елемента групе. На пример, ако имате наслов и опис непознате дужине, Barrier везан за десну ивицу најширег текста омогућава постављање иконе одмах иза њих. Атрибути: app:barrierDirection (left, right, top, bottom, start, end) и app:constraint_referenced_ids (листа id-ова одвојених зарезом).
Group — виртуелни контејнер који управља видљивошћу (visibility) више View-ова истовремено. Уместо да позивате setVisibility за сваки елемент појединачно, довољно је променити видљивост једне Group. Group не утиче на позиционирање — само на видљивост. Flow — виртуелни помоћник за креирање „течних" изгледа: елементи се аутоматски пребацују у нови ред/колону када нема места, као текст у пасусу. Flow подржава wrapMode: none, chain и aligned.
Ови алати (Barrier, Group, Flow, Guideline) називају се виртуелним помоћницима јер нису View-ови у класичном смислу — не заузимају место у хијерархији и не учествују у фокусу или додирним догађајима. Њихов циљ је да поједноставе одржавање сложених изгледа без додавања угнежђених контејнера.
Једноставна форма за пријаву са пољем за е-пошту, лозинком и дугметом. Сви елементи су везани за parent, осим дугмета — оно је испод поља за лозинку. Користи се равна хијерархија — сва три елемента на истом нивоу.
<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="Пријава" />
</androidx.constraintlayout.widget.ConstraintLayout>
Сви елементи имају ширину 0dp (match_constraint), односно протежу се од start до end констрејнта са уважавањем хоризонталне маргине. Ово је аналог match_parent-а са размацима, али без угнежђивања.
Три дугмета, равномерно распоређена хоризонтално са размацима од ивица. Ланац spread_inside поставља ивична дугмад на ивице, а средње — у центар између њих.
<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="Лево" />
<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="Центар" />
<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="Десно" />
Ланац се аутоматски ствара када елементи имају двосмерне констрејнте. Режим spread_inside се задаје на било ком елементу ланца преко app:layout_constraintHorizontal_chainStyle="spread_inside". Ово елиминише потребу за коришћењем LinearLayout-а са weightSum и layout_weight.
Креирање две једнаке колоне преко вертикалног Guideline-а на 50%. Леви елемент је везан за леву ивицу parent и десном ивицом за guideline; десни — левом ивицом за guideline и за десну ивицу 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 са процентом 0.5 се аутоматски прилагођава ширини екрана. На таблету и телефону однос колона остаје 50/50. За леву/десну номенклатуру користите start/end атрибуте за RTL компатибилност.
Табела поређења три главне ViewGroup за Android развој: ConstraintLayout, LinearLayout и RelativeLayout. Критеријуми: флексибилност, перформансе, сложеност кода и области примене.
| Карактеристика | ConstraintLayout | LinearLayout | RelativeLayout |
|---|---|---|---|
| Угнежђивање | Равно (један ниво) | Захтева угнежђивање за сложене изгледе | Један ниво, али ограничена флексибилност |
| Перформансе measure | 2 пролаза (~40% брже) | 4+ пролаза код weight | 2 пролаза |
| Процентуалне величине | Да (guide_percent, width_percent) | Само кроз weight/frame | Не |
| RTL подршка | Уграђена (start/end) | Уграђена | Преко start/end (API 17+) |
| Barrier/Group/Flow | Да (виртуелни помоћници) | Не | Не |
| MotionLayout анимације | Да | Не | Не |
| Када користити | Сви сложени изгледи, екрани са >5 елемената | Једноставне једносмерне листе, редови са дугмадима | Једноставни релативни изгледи (из legacy кода) |
Према подацима Android Vitals (Google, 2025), апликације које користе ConstraintLayout као главни контејнер у просеку показују 18% мање jank-оквира при приказивању сложених екрана у поређењу са апликацијама на угнежђеним LinearLayout-има. У IT Sectr-у смо прешли на ConstraintLayout као стандард за све XML изгледе 2018. године — ово је смањило просечну дубину хијерархије екрана са 4.2 на 1.8 нивоа и убрзало развој нових форми за 25%.
Често постављана питања
match_parent у ConstraintLayout-у ради као и обично — развлачи View до величине родитеља. 0dp (match_constraint) значи да се величина View-а израчунава из констрејната: ако су задати леви и десни констрејнти са маргином, ширина = parent — marginLeft — marginRight. Разлика у понашању: match_parent игнорише bias и може изаћи ван граница при анимацији; match_constraint коректно уважава сва ограничења и препоручује га Google као основни режим за ConstraintLayout.
Користите комбинацију: процентуалне величине (layout_constraintWidth_percent) за елементе који треба да заузимају део екрана; Guideline са процентима за поделу екрана на зоне; Barrier за позиционирање у односу на динамички садржај; Flow са wrapMode за пребацивање картица у нови ред. Алтернативни приступ — коришћење SlidingPaneLayout-а у комбинацији са ConstraintLayout-ом за master-detail интерфејсе на таблетима.
Jetpack Compose не користи ConstraintLayout као ViewGroup, али пружа Compose верзију ConstraintLayout-а (androidx.constraintlayout:constraintlayout-compose) са истим API-јем у Kotlin DSL-у: createRefFor(), constrainAs(), linkTo(), chain(), guideFrom(). Ово је корисно за сложене изгледе које је лакше описати кроз констрејнте него кроз Column/Row. Међутим, у Compose-у се препоручује почети са Column/Row/Box и прећи на ConstraintLayout само када су потребна сложена релативна позиционирања.
У Android Studio отворите Layout Inspector (Tools → Layout Inspector), изаберите покренуту апликацију и пређите курсором преко проблематичног елемента. Видећете све констрејнте, маргине, padding и bias у тродимензионалном приказу. За XML користите панел Design у едитору изгледа — он означава конфликте констрејната жутом бојом, а одсуство констрејната црвеном. У коду проверите да сваки View има два окомита констрејнта, иначе ће елемент завршити на (0,0).
Резиме
Развићемо мобилну апликацију под кључ
IT Sectr креира iOS и Android апликације за стартапе и предузећа од 2017. године. Саветоваћемо вас и предложити најбоље решење.
Прочитајте такође