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

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

Научете ключовите понятия за FrameLayout — най-простата ViewGroup в Android SDK, предназначена за поставяне на един дъщерен елемент (обикновено) или наслагване на няколко елемента един върху друг. FrameLayout е удобен за placeholder контейнери, фрагменти, индикатори за зареждане и елементи на преден план. Той не управлява разположението на децата — всеки следващ елемент се рисува върху предишния, а позицията им се определя от 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, проектирана за блокиране на област от екрана и показване на един дъщерен изглед (или наслагване на няколко). За разлика от LinearLayout (последователно поставяне) и RelativeLayout (относително позициониране), FrameLayout не променя позицията на децата — всяко ново child по подразбиране се поставя в горния ляв ъгъл (0,0) и се рисува върху предишното.

FrameLayout се появи в API Level 1 и остава най-лекият Android контейнер: той не презаписва onMeasure със сложна логика и извършва минимален брой layout операции. Според Android Performance Blog, FrameLayout изпълнява onLayout за едно преминаване и практически не добавя допълнителни разходи в сравнение с директното поставяне на View. Това го прави идеален избор за контейнери, където скоростта е важна: layout-и на елементи в RecyclerView (в комбинация с ConstraintLayout за позициониране), контейнери за фрагменти, overlay слоеве.

Размерът по подразбиране на FrameLayout се определя от най-големия дъщерен елемент (ако не е зададен match_parent). Ако не е посочен никакъв child, FrameLayout се свива до (0,0). Атрибутът android:measureAllChildren (раздел по-долу) променя това поведение.

FrameLayout в йерархията на Android ViewGroup

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

Foreground и measureAllChildren

android:foreground — атрибут на FrameLayout, който задава drawable, рисуван върху всички дъщерни елементи. За разлика от background (под децата), foreground се показва над съдържанието и може да бъде прозрачен. Използва се за: overlay ефекти при натискане (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 и 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: Контейнер за фрагменти

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() напълно заменя съдържанието, без да засяга активността.

Кога да използваме FrameLayout

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

Не е подходящ за: сложно позициониране на множество елементи (използвайте ConstraintLayout), последователни списъци (LinearLayout или RecyclerView), динамични оформления с промяна на реда на елементите.

FrameLayout като коренен елемент на екрана — лоша практика, ако екранът съдържа повече от 2–3 елемента. За коренен контейнер използвайте ConstraintLayout — дава повече контрол върху позиционирането без загуба на производителност. Използвайте FrameLayout само за дъщерни блокове, където е необходимо наслагване или placeholder.

Често задавани въпроси

Каква е разликата между FrameLayout и ConstraintLayout?

FrameLayout — минималистичен контейнер без система за позициониране (само layout_gravity), предназначен за наслагване на елементи. ConstraintLayout — пълноценна система за оформление с ограничения, вериги, бариери, процентни размери и поддръжка на MotionLayout. FrameLayout е по-бърз за прости случаи (1 child или overlay); ConstraintLayout е задължителен за сложни оформления. Избор: FrameLayout за overlay за зареждане и контейнер за фрагменти; 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 — най-простата Android ViewGroup за наслагване на елементи и placeholder контейнери с минимални допълнителни разходи.
  • Наслагване на слоеве — всяко ново child се рисува върху предишното; последното декларирано в XML е най-отгоре.
  • Foreground — drawable върху всички деца за overlay ефекти, маскиране и ripple анимация.
  • measureAllChildren — изключване (false) изключва GONE елементите от изчисляването на размера, ускорявайки рендирането с 20–60%.
  • layout_gravity — единственият механизъм за позициониране: top, bottom, left, right, center и техните комбинации.
  • Контейнер за фрагменти — най-честият случай на употреба: FrameLayout (или FragmentContainerView) за FragmentTransaction.replace().
  • Производителност — най-бързият контейнер за 1 child; за сложни оформления отстъпва на ConstraintLayout.

Ще разработим мобилно приложение под ключ

IT Sectr създава iOS и Android приложения за стартъпи и бизнеси от 2017 г. Ще ви консултираме и ще предложим най-доброто решение.

Обсъдете проекта

Прочетете също