Объясняем основы 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. Пример: button с layout_gravity="right" в VERTICAL-макете прижмёт кнопку к правому краю.
Разница: gravity — свойство родителя (как дети располагаются внутри него), layout_gravity — свойство ребёнка (как он себя ведёт внутри родителя). Ошибка — перепутать эти атрибуты — одна из самых частых у начинающих.
Для HORIZONTAL-макетов с текстовыми элементами (TextView, Button, EditText) полезен android:baselineAligned="true" — он выравнивает все тексты по базовой линии шрифта, независимо от их padding, size или height. Это делает строку с иконкой, заголовком и описанием визуально аккуратной: текст не прыгает вверх-вниз.
layout_weight — атрибут дочернего элемента LinearLayout, определяющий, какую долю свободного пространства он займёт. Работает только в направлении ориентации (ширина для HORIZONTAL, высота для VERTICAL). Формула расчёта: размер = собственный размер + (свободное пространство × (weight элемента / sum всех weight)). Свободное пространство = размер родителя — сумма всех собственных размеров детей.
layout_weight эффективнее фиксированных размеров для адаптивных интерфейсов. Например, если нужно, чтобы левая панель занимала 30% экрана, а правая — 70%, задайте weight=3 и weight=7. Для корректной работы обычно устанавливают размер по направлению в 0dp (android:layout_width="0dp" для HORIZONTAL), тогда собственный размер элемента считается нулевым, и весь размер определяется только weight. Это стандартная рекомендация Google (Android Developers Guide, 2024).
Важно: weight суммируются для всех элементов в 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), каждый дополнительный уровень вложенности увеличивает время 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 года. Мы проконсультируем вас и предложим наилучшее решение.
Читайте также