Poznaj kluczowe pojęcia FrameLayout — najprostszą ViewGroup w Android SDK, przeznaczoną do umieszczania jednego elementu potomnego (zwykle) lub nakładania kilku elementów na siebie. FrameLayout jest wygodny dla kontenerów zastępczych, fragmentów, wskaźników ładowania i elementów pierwszego planu. Nie zarządza rozmieszczeniem dzieci — każdy kolejny element rysowany jest nad poprzednim, a ich pozycja określana jest przez layout_gravity (left, top, right, bottom, center). Podstawowe scenariusze opisane są w FrameLayout API Reference.
Najważniejsze
FrameLayout to najprostsza ViewGroup w Android SDK, zaprojektowana do blokowania obszaru ekranu i wyświetlania jednego widoku potomnego (lub nakładania kilku). W przeciwieństwie do LinearLayout (sekwencyjne rozmieszczanie) i RelativeLayout (względne pozycjonowanie), FrameLayout nie zmienia pozycji dzieci — każdy nowy child umieszczany jest domyślnie w lewym górnym rogu (0,0) i rysowany nad poprzednim.
FrameLayout pojawił się w API Level 1 i pozostaje najlżejszym kontenerem Androida: nie nadpisuje onMeasure złożoną logiką i wykonuje minimalną liczbę operacji layout. Według Android Performance Blog, FrameLayout wykonuje onLayout w jednym przebiegu i praktycznie nie dodaje narzutu w porównaniu z bezpośrednim umieszczeniem View. Czyni to go idealnym wyborem dla kontenerów, gdzie liczy się szybkość: layouty itemów RecyclerView (w parze z ConstraintLayout do pozycjonowania), kontenery fragmentów, warstwy nakładkowe.
Rozmiar FrameLayout domyślnie określany jest przez największy element potomny (jeśli nie ustawiono match_parent). Jeśli nie określono żadnego child, FrameLayout zwija się do (0,0). Atrybut android:measureAllChildren (sekcja poniżej) zmienia to zachowanie.
FrameLayout dziedziczy po ViewGroup i jest bezpośrednim rodzicem wielu wyspecjalizowanych kontenerów: FragmentContainerView (fragmenty), CardView (karty z cieniem), ScrollView (z jednym child), NestedScrollView. Podczas tworzenia Activity z nawigacją Fragment standardowy szablon Android Studio używa FrameLayout (lub FragmentContainerView) jako głównego kontenera dla fragmentów.
android:foreground — atrybut FrameLayout, określający drawable rysowany nad wszystkimi elementami potomnymi. W przeciwieństwie do background (pod dziećmi), foreground wyświetlany jest nad zawartością i może być przezroczysty. Używany do: efektów nakładkowych przy naciśnięciu (ripple przez ?attr/selectableItemBackground), maskowania obrazów, wyświetlania wskaźników stanu (znacznik wyboru nad obrazkiem).
Foreground obsługuje standardowe zasoby drawable: ColorDrawable, ShapeDrawable, RippleDrawable, LayerDrawable. Od API 23+ dostępny jest android:foregroundGravity do pozycjonowania foreground (fill, center, top, bottom). W trybie „wypełnij" (fill) foreground rozciąga się na cały FrameLayout; w trybie center — rysowany jest na środku.
android:measureAllChildren — atrybut logiczny (true domyślnie), określający czy należy mierzyć wszystkie elementy potomne przy obliczaniu rozmiaru FrameLayout. Jeśli true (domyślnie), FrameLayout uwzględnia rozmiary wszystkich dzieci, włączając GONE (o rozmiarze 0). Jeśli false, FrameLayout mierzy tylko widoczne (VISIBLE) i niewidoczne (INVISIBLE) dzieci — elementy GONE są wykluczane z obliczeń. Według Google I/O 2019, wyłączenie measureAllChildren dla kontenerów z dużą liczbą elementów GONE (np. listy z przełączaniem widoczności) przyspiesza początkowe renderowanie o 20–60%.
android:layout_gravity — atrybut elementu potomnego FrameLayout (i innych ViewGroup), określający jego pozycję wewnątrz kontenera. W FrameLayout layout_gravity to jedyny sposób zarządzania położeniem elementu potomnego, ponieważ FrameLayout nie udostępnia własnych reguł pozycjonowania (jak RelativeLayout) ani kierunku (jak LinearLayout).
Możliwe wartości: top, bottom, left, right, center, center_horizontal, center_vertical, fill, fill_horizontal, fill_vertical, clip_horizontal, clip_vertical. Łączy się przez |: android:layout_gravity="bottom|center_horizontal" — element jest dociskany do dolnej krawędzi i wyśrodkowany poziomo. Dla elementów, których rozmiar jest mniejszy niż FrameLayout, layout_gravity określa ich pozycję w wolnej przestrzeni.
Jeśli layout_gravity nie jest ustawiony, domyślnie element umieszczany jest w lewym górnym rogu (top|left). Dla FrameLayout zawierającego kilka dzieci, każde może mieć własny layout_gravity — jeden element może być w lewym górnym rogu, drugi w prawym dolnym, trzeci na środku. Pozwala to tworzyć proste nakładania (np. krzyżyk zamykania nad obrazkiem).
android:gravity (atrybut rodzica) wyrównuje zawartość wewnątrz FrameLayout — na przykład tekst wewnątrz TextView. android:layout_gravity (atrybut dziecka) wyrównuje sam element wewnątrz FrameLayout. W kontekście FrameLayout gravity określa jak dzieci są rozmieszczane domyślnie (odpowiednik layout_gravity dla wszystkich dzieci naraz), ale layout_gravity każdego konkretnego elementu nadpisuje wartość rodzica.
Obraz i etykieta tekstowa w prawym dolnym rogu. FrameLayout zawiera ImageView na cały ekran i TextView z layout_gravity="bottom|end" do pozycjonowania na wierzchu.
<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 zajmuje cały FrameLayout (200dp wysokości). TextView z layout_gravity="bottom|end" umieszczany jest w prawym dolnym rogu nad obrazem. elevation=2dp dodaje cień pod etykietą, oddzielając ją wizualnie od obrazu. To minimalny przykład, który w LinearLayout wymagałby zagnieżdżonego kontenera lub kodu niestandardowego.
Ekran z treścią i paskiem postępu na środku, który pojawia się podczas ładowania. FrameLayout zawiera dwa elementy: treść i ProgressBar z visibility="gone" (przełączany w kodzie).
<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="Załadowana treść"
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 domyślnie ukryty (gone). Po rozpoczęciu ładowania w kodzie wywoływane jest findViewById(R.id.loading_spinner).visibility = View.VISIBLE — spinner pojawia się na środku nad treścią. Po zakończeniu ładowania — .visibility = View.GONE. FrameLayout zapewnia nakładanie bez zmiany pozycji treści — tekst nie przesuwa się przy pojawieniu się spinnera, ponieważ ProgressBar rysowany jest na wierzchu.
FrameLayout jako standardowy kontener dla FragmentTransaction. Aktywność zastępuje fragmenty wewnątrz tego kontenera w zależności od nawigacji.
<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 jako kontener fragmentów — najlżejszy sposób obsługi Fragment Navigation. FragmentContainerView (dziedziczący po FrameLayout) jest zalecany w Navigation Component 2.4+, ale prosty FrameLayout pozostaje prawidłowy dla ręcznych FragmentTransaction. Kluczową zaletą jest to, że FragmentTransaction.replace() całkowicie zastępuje zawartość, nie wpływając na Activity.
FrameLayout jest optymalny dla czterech scenariuszy: kontener fragmentów (FragmentContainerView lub FrameLayout), nakładka ładowania (ProgressBar nad treścią), etykiety na kartach (znacznik na obrazie), kontener zastępczy dla ViewStub (leniwe ładowanie).
Nie nadaje się do: złożonego pozycjonowania wielu elementów (użyj ConstraintLayout), list sekwencyjnych (LinearLayout lub RecyclerView), dynamicznych układów ze zmianą kolejności elementów.
FrameLayout jako główny element ekranu — zła praktyka, jeśli ekran zawiera więcej niż 2–3 elementy. Jako główny kontener używaj ConstraintLayout — daje więcej kontroli nad pozycjonowaniem bez utraty wydajności. FrameLayout używaj tylko dla bloków potomnych, gdzie potrzebne jest nakładanie lub placeholder.
Często zadawane pytania
FrameLayout — minimalistyczny kontener bez systemu pozycjonowania (tylko layout_gravity), przeznaczony do nakładania elementów. ConstraintLayout — pełnoprawny system układu z ograniczeniami, łańcuchami, barierami, procentowymi rozmiarami i obsługą MotionLayout. FrameLayout jest szybszy dla prostych przypadków (1 child lub overlay); ConstraintLayout jest obowiązkowy dla złożonych układów. Wybór: FrameLayout do nakładki ładowania i kontenera fragmentów; ConstraintLayout do wszystkiego innego.
Ustaw FrameLayout rozmiar wrap_content na potrzebnej osi (android:layout_width="wrap_content" i/lub android:layout_height="wrap_content"). W tym przypadku FrameLayout dostosuje się do rozmiaru największego elementu potomnego. Jeśli wszystkie dzieci są mniejsze niż FrameLayout, nadmiarowa przestrzeń pozostanie pusta. Do precyzyjnej kontroli używaj match_parent ze stałymi padding.
To normalne zachowanie FrameLayout — nie rozdziela on dzieci w przestrzeni, lecz rysuje je kolejno jedno na drugim. Jeśli chcesz, aby elementy nie nakładały się, użyj innego kontenera (LinearLayout, ConstraintLayout). Do częściowego nakładania z kontrolą kolejności rysowania używaj layout_gravity do przesunięcia i elevation dla Z-order.
FrameLayout jest szybszy dla maksymalnie prostych przypadków — jeden element potomny, bez złożonego pozycjonowania. FrameLayout wykonuje onMeasure w minimalnym czasie, nie obliczając ograniczeń. Jednak różnica jest zauważalna tylko przy setkach powtórzeń (RecyclerView na dziesiątkach tysięcy elementów). Dla zwykłego ekranu (1–3 FrameLayout) różnica wynosi mikrosekundy i nie wpływa na UX. ConstraintLayout to uniwersalny wybór dla 95% zadań.
Podsumowanie
Opracujemy aplikację mobilną pod klucz
IT Sectr tworzy aplikacje na iOS i Androida dla startupów i firm od 2017 roku. Doradzimy Ci i zaproponujemy najlepsze rozwiązanie.
Przeczytaj również