Пояснюємо суть RelativeLayout — ViewGroup в Android SDK для позиціонування дочірніх елементів відносно один одного або відносно батьківського контейнера. На відміну від LinearLayout, який вибудовує елементи строго послідовно, RelativeLayout дозволяє розташувати будь-який елемент ліворуч, праворуч, вище, нижче або по центру відносно будь-якого іншого елемента за його ID. Це робить RelativeLayout зручним для перекривних і складних макетів без вкладеності. Основні правила описані в документації Android Developers.
Головне
RelativeLayout — ViewGroup, що з'явилася в Android SDK починаючи з API Level 1, яка позиціонує дочірні елементи через декларативні правила (layout rules) відносно батька або інших дочірніх елементів. Кожне правило задається XML-атрибутом виду android:layout_above, android:layout_below, android:layout_toLeftOf зі значенням ID цільового елемента.
До появи RelativeLayout розробники були змушені використовувати вкладені LinearLayout для кожного нестандартного позиціонування — це призводило до глибокої вкладеності та повільної відмальовки. RelativeLayout вирішив цю проблему, запропонувавши відносне позиціонування на одному рівні ієрархії. Однак його гнучкість обмежена порівняно з сучасним ConstraintLayout: RelativeLayout не підтримує ланцюги, бар'єри, напрямні та відсоткові розміри.
RelativeLayout має дві ключові особливості: один прохід onMeasure (на відміну від LinearLayout з weight, що вимагає двох проходів) та залежність від порядку оголошення — якщо елемент A посилається на елемент B, а B оголошений після A, B повинен мати відомий розмір на момент вимірювання A. Згідно з Android Developers Guide (2024), RelativeLayout виконує вимірювання за один прохід, якщо всі правила посилаються на вже виміряні елементи. В іншому випадку потрібен другий прохід, що знижує продуктивність.
RelativeLayout виправданий для простих макетів з 3–7 елементами, де потрібне відносне позиціонування (надпис над кнопкою, іконка праворуч від тексту, панель внизу екрана). Для нових проєктів рекомендується ConstraintLayout, але RelativeLayout все ще широко використовується в застарілому коді та бібліотеках.
RelativeLayout використовує два типи правил: relative-правила (позиціонування відносно іншого елемента) та align-правила (прив'язка до батька). Кожен дочірній елемент може мати необмежену кількість правил, але конфліктуючі правила (наприклад, alignParentTop та below іншого елемента) вирішуються на користь останнього оголошеного атрибута.
Relative-правила:
- android:layout_above="@id/target" — нижня межа елемента прив'язана до верхньої межі target.
- android:layout_below="@id/target" — верхня межа елемента прив'язана до нижньої межі target.
- android:layout_toLeftOf="@id/target" — права межа прив'язана до лівої межі target.
- android:layout_toRightOf="@id/target" — ліва межа прив'язана до правої межі target.
- android:layout_toStartOf="@id/target" — аналог toLeftOf для RTL-локалізацій.
- android:layout_toEndOf="@id/target" — аналог toRightOf для RTL.
Align-правила (до батька):
- android:layout_alignParentTop="true" — верхній край елемента збігається з верхнім краєм батька.
- android:layout_alignParentBottom="true" — нижній край збігається з нижнім краєм батька.
- android:layout_alignParentLeft="true" — лівий край збігається з лівим краєм батька.
- android:layout_alignParentRight="true" — правий край збігається з правим краєм батька.
- android:layout_alignParentStart та android:layout_alignParentEnd — для RTL.
Align-правила до інших елементів:
- android:layout_alignTop="@id/target" — верх елемента збігається з верхом target.
- android:layout_alignBottom, android:layout_alignLeft, android:layout_alignRight, android:layout_alignStart, android:layout_alignEnd — аналогічно.
- android:layout_alignBaseline="@id/target" — вирівнювання за базовою лінією тексту (корисно для рядків з різними розмірами шрифту).
Центрування в RelativeLayout задається трьома атрибутами: android:layout_centerInParent="true" (по центру батька за обома осями), android:layout_centerHorizontal="true" (по центру по горизонталі) та android:layout_centerVertical="true" (по центру по вертикалі). Центрування сумісне з іншими правилами: елемент може бути відцентрований по горизонталі й при цьому прив'язаний до нижнього краю батька.
Порядок накладання (Z-order) в RelativeLayout визначається порядком оголошення елементів в XML. Перший оголошений елемент малюється першим (буде нижче), останній — останнім (буде вище). Якщо потрібно змінити порядок накладання без зміни XML, використовуйте android:elevation (починаючи з API 21) — елементи з більшою elevation малюються поверх. Для версій нижче API 21 порядок накладання регулюється лише порядком оголошення.
Важливо: RelativeLayout не підтримує атрибут android:elevation для батька — кожен елемент керує своєю elevation самостійно. При конфлікті правил позиціонування та elevation (наприклад, перекривні елементи) elevation має пріоритет для відмальовки, але не для позиціонування — елемент з більшою elevation може візуально перекривати інший, навіть якщо його layout-правила ставлять його «позаду».
Аватар по центру вгорі, ім'я під аватаром, статус під іменем, кнопка дії внизу екрана. Всі елементи використовують relative-правила для послідовної прив'язки.
<RelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<ImageView
android:id="@+id/avatar"
android:layout_width="100dp"
android:layout_height="100dp"
android:layout_centerHorizontal="true"
android:layout_marginTop="32dp"
android:src="@drawable/avatar_placeholder" />
<TextView
android:id="@+id/user_name"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="@+id/avatar"
android:layout_centerHorizontal="true"
android:layout_marginTop="12dp"
android:text="Іван Іванов"
android:textSize="20sp"
android:textStyle="bold" />
<TextView
android:id="@+id/user_status"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="@+id/user_name"
android:layout_centerHorizontal="true"
android:text="Онлайн"
android:textColor="@color/green" />
<Button
android:id="@+id/edit_button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
android:layout_centerHorizontal="true"
android:layout_marginBottom="24dp"
android:text="Редагувати профіль" />
</RelativeLayout>
Аватар прив'язаний до верху через marginTop + centerHorizontal, ім'я — під аватаром через layout_below, статус — під іменем, кнопка — до нижнього краю батька через alignParentBottom. Всі елементи незалежні один від одного, немає ланцюжків та вкладеності.
Іконка з червоним бейджем (кружок з числом) у правому верхньому куті. Бейдж позиціонується відносно іконки через align до її країв з мінусовими margin для накладання.
<RelativeLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content">
<ImageView
android:id="@+id/icon"
android:layout_width="48dp"
android:layout_height="48dp"
android:src="@drawable/ic_notification" />
<TextView
android:id="@+id/badge"
android:layout_width="20dp"
android:layout_height="20dp"
android:layout_alignTop="@+id/icon"
android:layout_alignRight="@+id/icon"
android:layout_marginTop="-6dp"
android:layout_marginRight="-6dp"
android:gravity="center"
android:background="@drawable/badge_background"
android:text="3"
android:textColor="@android:color/white"
android:textSize="10sp" />
</RelativeLayout>
Бейдж прив'язаний до верхнього та правого краю іконки. Негативні margin (-6dp) зсувають бейдж назовні, створюючи ефект перекриття межі іконки. Це популярний патерн для сповіщень, який без RelativeLayout потребував би FrameLayout або кастомної відмальовки.
Форма логіну: логотип по центру вгорі, поле email під ним, поле пароля під email, кнопка «Увійти» під паролем, посилання «Реєстрація» притиснуте до низу. Всі елементи прив'язані послідовно через layout_below.
<RelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:padding="24dp">
<TextView
android:id="@+id/logo"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerHorizontal="true"
android:layout_marginTop="48dp"
android:text="MyApp"
android:textSize="32sp"
android:textStyle="bold" />
<EditText
android:id="@+id/email_input"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_below="@+id/logo"
android:layout_marginTop="32dp"
android:hint="Email" />
<EditText
android:id="@+id/password_input"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_below="@+id/email_input"
android:layout_marginTop="16dp"
android:hint="Пароль"
android:inputType="textPassword" />
<Button
android:id="@+id/login_button"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_below="@+id/password_input"
android:layout_marginTop="24dp"
android:text="Увійти" />
<TextView
android:id="@+id/register_link"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
android:layout_centerHorizontal="true"
android:layout_marginBottom="16dp"
android:text="Немає акаунта? Зареєструватися"
android:textColor="@color/primary" />
</RelativeLayout>
Екран використовує 5 елементів на одному рівні ієрархії. Потік зверху вниз (логотип → email → пароль → кнопка) забезпечується через layout_below, а посилання притиснуте до низу через alignParentBottom. Для тієї ж функціональності LinearLayout потребував би два вкладених контейнери (один для верхньої групи, другий для посилання з weight).
Порівняння двох ViewGroup для відносного позиціонування: RelativeLayout (класична, API 1+) та ConstraintLayout (сучасна, Jetpack). Критерії: гнучкість, продуктивність, RTL-підтримка та адаптивність.
| Характеристика | RelativeLayout | ConstraintLayout |
|---|---|---|
| Відсоткові розміри | Ні (тільки фіксовані та match_parent) | Так (width_percent, height_percent, guideline_percent) |
| RTL-підтримка | Через start/end (API 17+) | Вбудована |
| Chain / ланцюжки | Ні | Так (spread, spread_inside, packed) |
| Barrier / Group / Flow | Ні | Так (віртуальні помічники) |
| MotionLayout анімації | Ні | Так |
| Проходи onMeasure | 1 (без циклів залежностей) | 2 (завжди, але оптимізовані) |
| Центрування | centerInParent, centerHorizontal, centerVertical | Через bias (0.0–1.0) з протилежними констрейнтами |
| Коли використовувати | Застарілий код, прості макети з 3–7 елементами | Нові проєкти, складні екрани, адаптивні макети |
RelativeLayout поступається ConstraintLayout практично за всіма параметрами, крім одного: він вбудований в Android SDK з API 1 і не вимагає додаткових залежностей. Для проєктів, які все ще використовують minSdk нижче API 14 (Android 4.0), RelativeLayout — єдиний стандартний варіант відносного позиціонування. В усіх інших випадках Google рекомендує ConstraintLayout.
Часті запитання
Найімовірніша причина — відсутність достатньої кількості правил позиціонування. Кожен елемент в RelativeLayout повинен мати хоча б одне правило по горизонталі (alignParentLeft, toRightOf, centerHorizontal) і одне по вертикалі (alignParentTop, below, centerVertical). Якщо правил немає, елемент опиняється в позиції (0,0) і може бути прихований під іншими елементами або за межами екрана. Друга причина — циклічна залежність (елемент A прив'язаний до B, B прив'язаний до A), яка призводить до непередбачуваної поведінки.
Використовуйте атрибут android:layout_centerInParent="true". Він центрує елемент за обома осями одночасно. Якщо потрібно тільки по горизонталі — android:layout_centerHorizontal="true", тільки по вертикалі — android:layout_centerVertical="true". Центрування працює в поєднанні з margin — елемент буде зміщений відносно центра на вказаний відступ.
Можна, але не рекомендується. RelativeLayout виконує два проходи onMeasure за наявності циклічних залежностей, що на 15–30% повільніше за ConstraintLayout у списках RecyclerView. Для item-макетів RecyclerView використовуйте ConstraintLayout — він забезпечує плоску ієрархію з мінімальним часом вимірювання. Якщо RelativeLayout вже використовується в застарілому проєкті, переконайтеся, що елементи не створюють циклічних залежностей, і виміряйте FPS скролу через Profile GPU Rendering.
android:layout_margin задає відступи з усіх сторін одночасно. android:layout_marginTop — тільки зверху. Пріоритет у більш специфічного атрибута: якщо задані і layout_margin (16dp), і layout_marginTop (8dp), верхній відступ буде 8dp. В RelativeLayout margin працює відносно констрейнта, а не відносно сусіднього елемента: layout_marginTop при layout_below="@id/target" — це відступ від верхньої межі елемента до нижньої межі target.
Підсумки
Ми розробимо мобільний застосунок під ключ
IT Sectr створює застосунки для iOS та Android для стартапів і бізнесу з 2017 року. Ми проконсультуємо вас і запропонуємо найкраще рішення.
Читайте також