FrameLayout: ключові поняття, просте позиціонування в Android

Автор: IT Sectr Опубліковано: 2026-02-24 Час читання: 8 хв

Вивчіть ключові поняття FrameLayout — найпростіша ViewGroup в Android SDK, призначена для розміщення одного дочірнього елемента (зазвичай) або накладання кількох елементів один на одного. FrameLayout зручний для контейнерів-заглушок, фрагментів, індикаторів завантаження та елементів переднього плану. Він не керує розташуванням дітей — кожен наступний елемент малюється поверх попереднього, а їх позиція визначається layout_gravity (left, top, right, bottom, center). Базові сценарії описані в FrameLayout API Reference.

Головне

  • Накладання шарів — FrameLayout розміщує елементи послідовно один поверх одного; останній оголошений в XML малюється найвище.
  • Foreground — атрибут android:foreground дозволяє накласти drawable поверх усіх дочірніх елементів, корисно для ефектів і маскування.
  • measureAllChildren — за замовчуванням FrameLayout вимірює всіх дітей, навіть невидимих (GONE). Вимкнення прискорює малювання на 20–60%.
  • layout_gravity — єдиний спосіб позиціонування дочірнього елемента всередині FrameLayout: top, bottom, left, right, center.
  • Використання з Fragment — FrameLayout — стандартний контейнер для FragmentTransaction.replace(), що замінює вміст без перестворення активності.

Що таке FrameLayout?

FrameLayout — найпростіша ViewGroup в Android SDK, призначена для блокування області екрана та відображення одного дочірнього view (або накладання кількох). На відміну від LinearLayout (послідовне розміщення) та RelativeLayout (відносне позиціонування), FrameLayout не змінює позиції дітей — кожен новий child поміщається в лівий верхній кут (0,0) за замовчуванням і малюється поверх попереднього.

FrameLayout з'явився в API Level 1 і залишається найлегшим контейнером Android: він не перевизначає onMeasure складною логікою та виконує мінімальну кількість операцій layout. Згідно з Android Performance Blog, FrameLayout виконує onLayout за один прохід і практично не додає накладних витрат порівняно з розміщенням View безпосередньо. Це робить його ідеальним вибором для контейнерів, де важлива швидкість: макети елементів RecyclerView (в парі з ConstraintLayout для позиціонування), контейнери Fragment, шари накладання.

Розмір FrameLayout за замовчуванням визначається найбільшим дочірнім елементом (якщо не задано match_parent). Якщо жоден child не вказано, FrameLayout згортається до (0,0). Атрибут android:measureAllChildren (розділ нижче) змінює цю поведінку.

FrameLayout в ієрархії Android ViewGroup

FrameLayout успадковується від ViewGroup і є прямим батьком багатьох спеціалізованих контейнерів: FragmentContainerView (фрагменти), CardView (картки з тінню), ScrollView (один child), NestedScrollView. При створенні Activity з навігацією за фрагментами стандартний шаблон Android Studio використовує FrameLayout (або FragmentContainerView) як кореневий контейнер для фрагментів.

Foreground і measureAllChildren

android:foreground — атрибут FrameLayout, що задає drawable, який малюється поверх усіх дочірніх елементів. На відміну від background (під дітьми), foreground відображається над вмістом і може бути прозорим. Використовується для: ефектів накладання при натисканні (ripple через ?attr/selectableItemBackground), маскування зображень, відображення індикаторів стану (галочка вибору поверх картинки).

Foreground підтримує стандартні drawable-ресурси: ColorDrawable, ShapeDrawable, RippleDrawable, LayerDrawable. З API 23+ доступний android:foregroundGravity для позиціонування foreground (fill, center, top, bottom). У режимі «заповнення» (fill) foreground розтягується на весь FrameLayout; у режимі center — малюється в центрі.

android:measureAllChildren — булевий атрибут (true за замовчуванням), що визначає, чи потрібно вимірювати всіх дочірніх елементів при розрахунку розміру FrameLayout. Якщо true (за замовчуванням), FrameLayout враховує розміри всіх дітей, включаючи GONE (з розміром 0). Якщо false, FrameLayout вимірює лише видимі (VISIBLE) та невидимі (INVISIBLE) дітей — GONE-елементи виключаються з розрахунку. Згідно з Google I/O 2019, вимкнення measureAllChildren для контейнерів з великою кількістю GONE-елементів (наприклад, списки з перемиканням видимості) прискорює початкове малювання на 20–60%.

layout_gravity: позиціонування всередині FrameLayout

android:layout_gravity — атрибут дочірнього елемента FrameLayout (та інших ViewGroup), що визначає його позицію всередині контейнера. У FrameLayout layout_gravity — єдиний спосіб керування положенням дочірнього елемента, оскільки FrameLayout не надає власних правил позиціонування (як RelativeLayout) або напрямку (як LinearLayout).

Можливі значення: top, bottom, left, right, center, center_horizontal, center_vertical, fill, fill_horizontal, fill_vertical, clip_horizontal, clip_vertical. Комбінуються через |: android:layout_gravity="bottom|center_horizontal" — елемент притиснутий до нижнього краю та відцентрований по горизонталі. Для елементів, розмір яких менший за FrameLayout, layout_gravity визначає їх позицію у вільному просторі.

Якщо layout_gravity не задано, за замовчуванням елемент поміщається в лівий верхній кут (top|left). Для FrameLayout, що містить кілька дітей, кожен може мати свій layout_gravity — один елемент може бути в лівому верхньому куті, інший — у правому нижньому, третій — по центру. Це дозволяє створювати прості накладання (наприклад, хрестик закриття поверх картинки).

Різниця: gravity vs layout_gravity

android:gravity (батьківський атрибут) вирівнює вміст всередині FrameLayout — наприклад, текст всередині TextView. android:layout_gravity (дочірній атрибут) вирівнює сам елемент всередині FrameLayout. У контексті FrameLayout gravity задає, як діти розташовуються за замовчуванням (аналог layout_gravity для всіх дітей одразу), але layout_gravity кожного конкретного елемента перевизначає батьківське значення.

Приклади: XML і варіанти використання

Приклад 1: Базове накладання — мітка на зображенні

Зображення та текстова мітка в правому нижньому куті. FrameLayout містить ImageView на весь екран і TextView з layout_gravity="bottom|end" для позиціонування поверх зображення.

xml
<FrameLayout
    android:layout_width="match_parent"
    android:layout_height="200dp">

    <ImageView
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:src="@drawable/product_photo"
        android:scaleType="centerCrop" />

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="bottom|end"
        android:layout_margin="8dp"
        android:background="@drawable/badge_background"
        android:elevation="2dp"
        android:paddingHorizontal="8dp"
        android:paddingVertical="4dp"
        android:text="-30%"
        android:textColor="@android:color/white"
        android:textSize="14sp"
        android:textStyle="bold" />

</FrameLayout>

ImageView займає весь FrameLayout (висота 200dp). TextView з layout_gravity="bottom|end" розміщується в правому нижньому куті поверх зображення. elevation=2dp додає тінь під міткою, відокремлюючи її від зображення візуально. Цей мінімальний приклад в LinearLayout вимагав би вкладеного контейнера або кастомного коду.

Приклад 2: Індикатор завантаження (прогрес-бар поверх контенту)

Екран з контентом і прогрес-баром по центру, який з'являється під час завантаження. FrameLayout містить два елементи: контент і ProgressBar з visibility="gone" (перемикається в коді).

xml
<FrameLayout
    android:id="@+id/content_container"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <TextView
        android:id="@+id/content_text"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:gravity="center"
        android:text="Завантажений контент"
        android:textSize="18sp" />

    <ProgressBar
        android:id="@+id/loading_spinner"
        android:layout_width="48dp"
        android:layout_height="48dp"
        android:layout_gravity="center"
        android:visibility="gone" />

</FrameLayout>

ProgressBar за замовчуванням прихований (gone). При старті завантаження в коді викликається findViewById(R.id.loading_spinner).visibility = View.VISIBLE — спіннер з'являється по центру поверх контенту. Після завантаження — .visibility = View.GONE. FrameLayout забезпечує накладання без зміни позиції контенту — текст не зсувається при появі спіннера, оскільки ProgressBar малюється поверх.

Приклад 3: Контейнер Fragment

FrameLayout як стандартний контейнер для FragmentTransaction. Активність замінює фрагменти всередині цього контейнера залежно від навігації.

xml
<FrameLayout
    android:id="@+id/fragment_container"
    android:layout_width="match_parent"
    android:layout_height="match_parent" />
kotlin
import androidx.fragment.app.FragmentTransaction

val fragmentContainer = R.id.fragment_container

fun navigateTo(fragment: Fragment) {
    supportFragmentManager
        .beginTransaction()
        .setTransition(FragmentTransaction.TRANSIT_FRAGMENT_FADE)
        .replace(fragmentContainer, fragment)
        .addToBackStack(null)
        .commit()
}

FrameLayout як контейнер фрагментів — найлегший спосіб підтримки Fragment Navigation. FragmentContainerView (спадкоємець FrameLayout) рекомендується з Navigation Component 2.4+, але простий FrameLayout залишається валідним для ручних FragmentTransaction. Ключова перевага — FragmentTransaction.replace() повністю замінює вміст, не зачіпаючи Activity.

Коли використовувати FrameLayout

FrameLayout оптимальний для чотирьох сценаріїв: контейнер фрагментів (FragmentContainerView або FrameLayout), loading overlay (ProgressBar поверх контенту), карткові мітки (мітка на зображенні), контейнер-заглушка для ViewStub (ліниве завантаження).

Не підходить для: складного позиціонування кількох елементів (використовуйте ConstraintLayout), послідовних списків (LinearLayout або RecyclerView), динамічних макетів зі зміною порядку елементів.

FrameLayout як кореневий елемент екрана — погана практика, якщо екран містить більше 2–3 елементів. Для кореневого контейнера використовуйте ConstraintLayout — він дає більше контролю над позиціонуванням без втрати продуктивності. FrameLayout використовуйте лише для дочірніх блоків, де потрібно накладання або заглушка.

Поширені запитання

У чому різниця між FrameLayout та ConstraintLayout?

FrameLayout — мінімалістичний контейнер без системи позиціонування (тільки layout_gravity), призначений для накладання елементів. ConstraintLayout — повноцінна система верстки з констрейнтами, ланцюгами, бар'єрами, відсотковими розмірами та підтримкою MotionLayout. FrameLayout швидший для простих випадків (1 child або overlay); ConstraintLayout обов'язковий для складних макетів. Вибір: FrameLayout для loading overlay та fragment-container; ConstraintLayout для всього іншого.

Як зробити, щоб FrameLayout займав лише місце найбільшого child?

Встановіть FrameLayout розмір wrap_content за потрібною віссю (android:layout_width="wrap_content" та/або android:layout_height="wrap_content"). У цьому випадку FrameLayout підлаштується під розмір найбільшого дочірнього елемента. Якщо всі діти менші за FrameLayout, зайвий простір залишиться пустим. Для точного контролю використовуйте match_parent з фіксованими padding.

Чому елементи в FrameLayout перекривають один одного?

Це штатна поведінка FrameLayout — він не розподіляє дітей у просторі, а малює їх послідовно один поверх одного. Якщо ви хочете, щоб елементи не перекривалися, використовуйте інший контейнер (LinearLayout, ConstraintLayout). Для часткового перекриття з контролем порядку малювання використовуйте layout_gravity для зсуву та elevation для Z-порядку.

Що швидше: FrameLayout чи ConstraintLayout?

FrameLayout швидший для максимально простих випадків — один дочірній елемент, без складного позиціонування. FrameLayout виконує onMeasure за мінімальний час, не обчислюючи констрейнтів. Однак різниця помітна лише при сотнях повторень (RecyclerView на десятках тисяч елементів). Для звичайного екрана (1–3 FrameLayout) різниця в мікросекундах і не впливає на UX. ConstraintLayout — універсальний вибір для 95% завдань.

Підсумки

  • FrameLayout — найпростіша ViewGroup Android для накладання елементів та контейнерів-заглушок з мінімальними накладними витратами.
  • Накладання шарів — кожен новий child малюється поверх попереднього; останній оголошений в XML — найвищий.
  • Foreground — drawable поверх усіх дітей для ефектів накладання, маскування та ripple-анімації.
  • measureAllChildren — вимкнення (false) виключає GONE-елементи з розрахунку розміру, прискорюючи малювання на 20–60%.
  • layout_gravity — єдиний механізм позиціонування: top, bottom, left, right, center та їх комбінації.
  • Fragment-контейнер — найчастіший use case: FrameLayout (або FragmentContainerView) для FragmentTransaction.replace().
  • Продуктивність — найшвидший контейнер для 1 child; для складних макетів поступається ConstraintLayout.

Ми розробимо мобільний застосунок під ключ

IT Sectr створює застосунки для iOS та Android для стартапів і бізнесу з 2017 року. Ми проконсультуємо вас і запропонуємо найкраще рішення.

Обговорити проект

Читайте також