Објашњавамо основе LinearLayout — основне ViewGroup у Android SDK за поравнање ћерки елемената у једну линију: вертикално (LinearLayout.VERTICAL) или хоризонтално (LinearLayout.HORIZONTAL). LinearLayout остаје најједноставнији и најбржи начин за изградњу реда дугмади, листе подешавања или формулара за унос, када сложеност распореда не прелази 5–10 елемената. Атрибути layout_weight и orientation управљају расподелом простора и смером. Детаљна спецификација — у LinearLayout API Reference.
Главне тачке
LinearLayout — једна од првих ViewGroup у Android SDK (API Level 1), намењена за узастопно постављање ћерки елемената у једном реду или колони. Сваки нови елемент се поставља после претходног, а смер се задаје атрибутом android:orientation. LinearLayout је најједноставнији и најпредвидљивији Android контејнер, што га чини идеалним избором за линеарне интерфејсе: редови дугмади, листе подешавања са иконом и текстом, хоризонтални менији.
Историјски, LinearLayout је први ViewGroup са којим се упознаје почетник Android програмер. Његова предност је транспарентност: ћерки елементи се постављају строго по редоследу декларације, без скривених правила позиционирања. За разлику од RelativeLayout, где елемент може бити повезан са било којим суседом, или ConstraintLayout са његовим системом ограничења, LinearLayout ради по принципу „први лево/горе, други после њега, трећи после другог“. Према Google I/O 2017, 78% екрана у топ 1000 апликација Google Play-а користи LinearLayout као главни или помоћни контејнер.
Главна метрика LinearLayout — LinearLayout.MeasureSpec — систем управљања величином. При оријентацији VERTICAL ширина сваког елемента је једнака ширини родитеља (match_parent или фиксна), а висина се одређује садржајем или атрибутом. За HORIZONTAL обрнуто: висина је једнака родитељској, ширина — по садржају. Ако у HORIZONTAL реду укупна ширина елемената премаши ширину родитеља, елементи могу изаћи ван екрана (ако се не користи weight).
LinearLayout преписује onMeasure и onLayout из ViewGroup. При VERTICAL оријентацији onLayout поставља елементе на Y оси: први child — y=0, други — y=prev.bottom + divider.hidden, и тако даље. При HORIZONTAL — на X оси. Divider (раздвојник) се задаје кроз android:divider и android:showDividers (beginning, middle, end). Раздвојник може бити боја, drawable или прилагођени Shape ресурс.
Оријентација — кључни атрибут LinearLayout који одређује смер постављања ћерки елемената. android:orientation="vertical" поставља елементе одозго надоле; android:orientation="horizontal" — слева надесно (или здесна налево при RTL локализацији). Ако оријентација није задата, подразумевано се користи HORIZONTAL (на старијим верзијама) — али експлицитно навођење је обавезно за предвидљиво понашање.
android:gravity — поравнање садржаја унутар LinearLayout. Прима комбинације: top, bottom, left, right, center, center_horizontal, center_vertical, fill, clip_vertical, clip_horizontal. Вредност gravity="center" за VERTICAL распоред ће поставити све елементе центрирано хоризонтално. android:layout_gravity — атрибут ћерки елемента који га поравнава унутар родитељског LinearLayout. Пример: дугме са layout_gravity="right" у VERTICAL распореду биће прилепљено уз десну ивицу.
Разлика: gravity — својство родитеља (како су деца распоређена унутар њега), layout_gravity — својство детета (како се понаша унутар родитеља). Грешка мешања ових атрибута је једна од најчешћих код почетника.
За HORIZONTAL распореде са текстуалним елементима (TextView, Button, EditText) користан је android:baselineAligned="true" — поравнава све текстове по основној линији фонта, независно од њиховог padding, size или height. Ово чини ред са иконом, насловом и описом визуелно уредним: текст не скаче горе-доле.
layout_weight — атрибут ћерки елемента LinearLayout који одређује који део слободног простора ће заузети. Ради само у смеру оријентације (ширина за HORIZONTAL, висина за VERTICAL). Формула израчунавања: величина = сопствена величина + (слободан простор × (тежина елемента / збир свих тежина)). Слободан простор = величина родитеља — збир свих сопствених величина деце.
layout_weight је ефикаснији од фиксних величина за адаптивне интерфејсе. На пример, ако леви панел треба да заузима 30% екрана, а десни — 70%, поставите weight=3 и weight=7. За коректан рад обично се поставља величина у смеру на 0dp (android:layout_width="0dp" за HORIZONTAL), тада се сопствена величина елемента сматра нулом, а цела величина се одређује само путем weight. Ово је стандардна Google препорука (Android Developers Guide, 2024).
Важно: тежине се сабирају за све елементе у LinearLayout, укључујући оне без weight (њихова тежина се сматра 0). Ако један елемент има weight=1, а други weight=2, први ће заузети 1/3 слободног простора, други — 2/3. weightSum (атрибут родитеља) омогућава постављање максималног збира тежине — ако су тежине деце у збиру мање од weightSum, разлика остаје као празан простор.
Формулар за пријаву састоји се од три елемента: поље email (заузима 2/4), поље лозинке (1/4) и дугме (1/4) висине екрана. Сва три елемента са weight раде само у VERTICAL оријентацији по висини.
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:padding="16dp">
<EditText
android:id="@+id/email"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="2"
android:hint="Email" />
<EditText
android:id="@+id/password"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1"
android:hint="Лозинка"
android:inputType="textPassword" />
<Button
android:id="@+id/login_button"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1"
android:text="Пријава" />
</LinearLayout>
Висина сваког елемента = (parent_height — padding) × (weight / sumWeights). email ће добити 2/4 висине, лозинка и дугме — по 1/4. Сва height=0dp, па сопствена величина није узета у обзир — ради само weight.
Панел са три дугмета: икона назад (wrap_content), наслов (fill остало) и икона менија (wrap_content). weight за наслов = 1, дугмад без weight.
<LinearLayout
android:orientation="horizontal"
android:layout_width="match_parent"
android:layout_height="56dp"
android:gravity="center_vertical">
<ImageButton
android:id="@+id/btn_back"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="@drawable/ic_arrow_back" />
<TextView
android:id="@+id/toolbar_title"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="Наслов"
android:textSize="18sp" />
<ImageButton
android:id="@+id/btn_menu"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="@drawable/ic_more_vert" />
</LinearLayout>
Дугмад назад и мени заузимају тачно онолико колико је потребно за икону. Наслов (TextView) са width=0dp и weight=1 се растеже на сав преостали простор између њих. gravity="center_vertical" поравнава све елементе по вертикалном центру.
Картица производа: хоризонтални ред (слика + вертикални блок текста). Спољни LinearLayout HORIZONTAL, унутрашњи — VERTICAL за наслов, опис и цену.
<LinearLayout
android:orientation="horizontal"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:padding="8dp">
<ImageView
android:layout_width="80dp"
android:layout_height="80dp"
android:src="@drawable/product_image"
android:scaleType="centerCrop" />
<LinearLayout
android:orientation="vertical"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:layout_marginStart="12dp">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Назив производа"
android:textStyle="bold"
android:textSize="16sp" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Кратак опис производа у два реда"
android:textSize="14sp"
android:textColor="@color/gray" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="1 299 руб."
android:textSize="18sp"
android:textColor="@color/accent" />
</LinearLayout>
</LinearLayout>
Слика лево (80×80dp), десно — вертикални блок од три реда текста. Унутрашњи LinearLayout има weight=1, па заузима сав преостали простор после слике. Ово је класичан образац картице листе који се среће у хиљадама апликација.
Главни недостатак LinearLayout — потреба за угнежђеним контејнерима за сложене распореде. Ако је потребно поставити елементе у мрежу 2×2 или направити ред са поравнањем на две ивице, потребно је најмање 2–3 нивоа угнежђавања. Сваки ниво LinearLayout додаје 2 пролаза onMeasure, што на слабим уређајима доводи до приметних застоја при исцртавању.
Према Android Performance Blog (Google, 2017), сваки додатни ниво угнежђавања повећава време распореда за 30–50 μs. За листу од 20 елемената са 3 нивоа угнежђавања, кашњење може достићи 3 ms по елементу — то је приметан jank при скроловању. ConstraintLayout решава овај проблем кроз равну хијерархију, замењујући 3–4 угнежђена LinearLayout једним контејнером са ограничењима.
Препорука: користите LinearLayout за једноставне линеарне распореде (до 10 елемената, један ниво угнежђавања). За картице листи, формуларе са различитим поравнањем и екране са табеларном структуром бирајте ConstraintLayout. За елементе који се понављају у RecyclerView-у користите ConstraintLayout у layout фајлу елемента — ово је кључно за перформансе скроловања.
Често постављана питања
Најчешћи разлог — није постављена величина у смеру на 0dp. За HORIZONTAL распоред потребно је android:layout_width="0dp", за VERTICAL — android:layout_height="0dp". Ако елемент има wrap_content или match_parent, weight можда неће радити или дати неочекивани резултат. Други разлог — weightSum на родитељу је мањи од збира weight деце. Трећи — употреба match_parent на ћерки елементу у супротном смеру.
layout_weight се поставља на сваком ћерки елементу и одређује његов удео у слободном простору. weightSum — атрибут родитеља LinearLayout који поставља максимални збир тежина. Подразумевано, weightSum = збиру свих layout_weight деце. Ако поставите weightSum = 3, а децу са weight=1, свако ће заузети 1/3. Ако има само двоје деце са weight=1, преостала 1/3 ће бити празна. weightSum је згодан за фиксирање пропорција без прерачунавања при додавању/уклањању елемената.
Подразумевано нема размака — елементи се постављају један уз други. Ако су се размаци појавили, проверите: android:divider и android:showDividers на родитељу, android:layout_margin на ћерки елементима. Додатно може утицати android:weightSum — ако елементи користе weight, између њих може остати празан простор због нетачног израчунавања. За нулте размаке поставите свим ћерки елементима margin=0dp и уклоните divider.
За једноставне једносмерне распореде (1–5 елемената у реду/колони) LinearLayout је нешто бржи — не троши ресурсе на израчунавање ограничења. За сложене распореде (више од 5 елемената, мреже, различити типови поравнања) ConstraintLayout је бржи јер користи равну хијерархију уместо угнежђених LinearLayout. Према Google тестовима (Android Performance Blog), ConstraintLayout побеђује на распоредима који захтевају 3+ нивоа угнежђавања LinearLayout.
Закључци
Развићемо мобилну апликацију под кључ
IT Sectr креира iOS и Android апликације за стартапе и предузећа од 2017. године. Саветоваћемо вас и предложити најбоље решење.
Прочитајте такође