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

Автор: IT Sectr Опубликовано: 2026-02-24 Время чтения: 8 мин

Изучите ключевые понятия FrameLayout — простейшая ViewGroup в Android SDK, предназначенная для размещения одного дочернего элемента (обычно) или наложения нескольких элементов друг на друга. FrameLayout удобен для placeholder-контейнеров, фрагментов, loading-индикаторов и foreground-элементов. Он не управляет расположением детей — каждый следующий элемент рисуется поверх предыдущего, а их позиция определяется 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 напрямую. Это делает его идеальным выбором для контейнеров, где важна скорость: item-макеты RecyclerView (в паре с ConstraintLayout для позиционирования), Fragment-контейнеры, overlay-слои.

Размер FrameLayout по умолчанию определяется самым большим дочерним элементом (если не задан match_parent). Если ни один child не указан, FrameLayout схлопывается до (0,0). Атрибут android:measureAllChildren (раздел ниже) изменяет это поведение.

FrameLayout в иерархии Android ViewGroup

FrameLayout наследуется от ViewGroup и является прямым родителем многих специализированных контейнеров: FragmentContainerView (фрагменты), CardView (карточки с тенью), ScrollerView (с одним child), NestedScrollView. При создании Activity с Fragment-навигацией стандартный шаблон 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 и use cases

Пример 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: Loading-индикатор (прогресс бар поверх контента)

Экран с контентом и прогресс-баром по центру, который появляется при загрузке. 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-контейнер — самый лёгкий способ поддержки Fragment Navigation. FragmentContainerView (наследник FrameLayout) рекомендуется с Navigation Component 2.4+, но простой FrameLayout остаётся валидным для ручных FragmentTransaction. Ключевое преимущество — FragmentTransaction.replace() полностью заменяет содержимое, не затрагивая Activity.

Когда использовать FrameLayout

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

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

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

Часто задаваемые вопросы

В чём разница между 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-order.

Что быстрее: 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 года. Мы проконсультируем вас и предложим наилучшее решение.

Обсудить проект

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