Изучите ключевые понятия FrameLayout — простейшая ViewGroup в Android SDK, предназначенная для размещения одного дочернего элемента (обычно) или наложения нескольких элементов друг на друга. FrameLayout удобен для placeholder-контейнеров, фрагментов, loading-индикаторов и foreground-элементов. Он не управляет расположением детей — каждый следующий элемент рисуется поверх предыдущего, а их позиция определяется layout_gravity (left, top, right, bottom, center). Базовые сценарии описаны в FrameLayout API Reference.
Главное
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 наследуется от ViewGroup и является прямым родителем многих специализированных контейнеров: FragmentContainerView (фрагменты), CardView (карточки с тенью), ScrollerView (с одним child), NestedScrollView. При создании Activity с Fragment-навигацией стандартный шаблон Android Studio использует FrameLayout (или FragmentContainerView) как корневой контейнер для фрагментов.
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%.
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 — один элемент может быть в левом верхнем углу, другой — в правом нижнем, третий — по центру. Это позволяет создавать простые наложения (например, крестик закрытия поверх картинки).
android:gravity (родительский атрибут) выравнивает содержимое внутри FrameLayout — например, текст внутри TextView. android:layout_gravity (дочерний атрибут) выравнивает сам элемент внутри FrameLayout. В контексте FrameLayout gravity задаёт, как дети располагаются по умолчанию (аналог layout_gravity для всех детей сразу), но layout_gravity каждого конкретного элемента переопределяет родительское значение.
Изображение и текстовая метка в правом нижнем углу. FrameLayout содержит ImageView на весь экран и TextView с layout_gravity="bottom|end" для позиционирования поверх.
<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 потребовал бы вложенного контейнера или кастомного кода.
Экран с контентом и прогресс-баром по центру, который появляется при загрузке. FrameLayout содержит два элемента: контент и ProgressBar с visibility="gone" (переключается в коде).
<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 рисуется поверх.
FrameLayout как стандартный контейнер для FragmentTransaction. Активность заменяет фрагменты внутри этого контейнера в зависимости от навигации.
<FrameLayout
android:id="@+id/fragment_container"
android:layout_width="match_parent"
android:layout_height="match_parent" />
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 оптимален для четырёх сценариев: контейнер фрагментов (FragmentContainerView или FrameLayout), loading overlay (ProgressBar поверх контента), card-лейблы (метка на изображении), контейнер-заглушка для ViewStub (ленивая загрузка).
Не подходит для: сложного позиционирования нескольких элементов (используйте ConstraintLayout), последовательных списков (LinearLayout или RecyclerView), динамических макетов с изменением порядка элементов.
FrameLayout как корневой элемент экрана — плохая практика, если экран содержит больше 2–3 элементов. Для корневого контейнера используйте ConstraintLayout — он даёт больше контроля над позиционированием без потери производительности. FrameLayout используйте только для дочерних блоков, где нужно наложение или placeholder.
Часто задаваемые вопросы
FrameLayout — минималистичный контейнер без системы позиционирования (только layout_gravity), предназначенный для наложения элементов. ConstraintLayout — полноценная система вёрстки с констрейнтами, цепями, барьерами, процентными размерами и поддержкой MotionLayout. FrameLayout быстрее для простых случаев (1 child или overlay); ConstraintLayout обязателен для сложных макетов. Выбор: FrameLayout для loading overlay и fragment-container; ConstraintLayout для всего остального.
Установите FrameLayout размер wrap_content по нужной оси (android:layout_width="wrap_content" и/или android:layout_height="wrap_content"). В этом случае FrameLayout подстроится под размер самого большого дочернего элемента. Если все дети меньше FrameLayout, лишнее пространство останется пустым. Для точного контроля используйте match_parent с фиксированными padding.
Это штатное поведение FrameLayout — он не распределяет детей в пространстве, а рисует их последовательно один поверх другого. Если вы хотите, чтобы элементы не перекрывались, используйте другой контейнер (LinearLayout, ConstraintLayout). Для частичного перекрытия с контролем порядка рисовки используйте layout_gravity для смещения и elevation для Z-order.
FrameLayout быстрее для максимально простых случаев — один дочерний элемент, без сложного позиционирования. FrameLayout выполняет onMeasure за минимальное время, не вычисляя констрейнты. Однако разница заметна только при сотнях повторений (RecyclerView на десятках тысяч элементов). Для обычного экрана (1–3 FrameLayout) разница в микросекундах и не влияет на UX. ConstraintLayout — универсальный выбор для 95% задач.
Итоги
Мы разработаем мобильное приложение под ключ
IT Sectr создаёт приложения для iOS и Android для стартапов и бизнеса с 2017 года. Мы проконсультируем вас и предложим наилучшее решение.
Читайте также