Пояснюємо основи 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. Розділювач задається через 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), заголовок (заповнити решту) та іконка меню (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), кожен додатковий рівень вкладеності збільшує час layout на 30–50 мкс. Для списку з 20 елементів з 3 рівнями вкладеності затримка може досягати 3 мс на елемент — це помітний 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, а children з 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 року. Ми проконсультуємо вас і запропонуємо найкраще рішення.
Читайте також