Ismerje meg a FrameLayout alapfogalmait — a legegyszerűbb ViewGroup az Android SDK-ban, amely egy gyermekelem elhelyezésére (általában) vagy több elem egymásra helyezésére szolgál. A FrameLayout kényelmes helyőrző konténerekhez, fragmensekhez, betöltésjelzőkhöz és előtér-elemekhez. Nem kezeli a gyermekek elhelyezését — minden következő elem az előző fölé rajzolódik, pozíciójukat pedig a layout_gravity határozza meg (left, top, right, bottom, center). Az alapszcenáriók a FrameLayout API Reference-ben találhatók.
Főbb pontok
A FrameLayout a legegyszerűbb ViewGroup az Android SDK-ban, amely egy képernyőterület blokkolására és egy gyermek nézet megjelenítésére (vagy több egymásra helyezésére) szolgál. A LinearLayout-tól (szekvenciális elhelyezés) és a RelativeLayout-tól (relatív pozicionálás) eltérően a FrameLayout nem változtatja meg a gyermekek pozícióját — minden új child alapértelmezés szerint a bal felső sarokba (0,0) kerül, és az előző fölé rajzolódik.
A FrameLayout az API Level 1-ben jelent meg, és a mai napig a legkönnyebb Android konténer maradt: nem írja felül az onMeasure-t összetett logikával, és minimális számú layout műveletet végez. Az Android Performance Blog szerint a FrameLayout egy menetben végrehajtja az onLayout-ot, és gyakorlatilag nincs többletterhelése a View közvetlen elhelyezéséhez képest. Ez ideális választássá teszi olyan konténerekhez, ahol a sebesség számít: RecyclerView item-layoutok (ConstraintLayout-tal párosítva a pozicionáláshoz), fragment konténerek, átfedő rétegek.
A FrameLayout alapértelmezett méretét a legnagyobb gyermekelem határozza meg (ha nincs match_parent beállítva). Ha nincs megadva child, a FrameLayout (0,0)-ra zsugorodik. Az android:measureAllChildren attribútum (lásd alább) megváltoztatja ezt a viselkedést.
A FrameLayout a ViewGroup-ból származik, és számos speciális konténer közvetlen szülője: FragmentContainerView (fragmensek), CardView (árnyékos kártyák), ScrollView (egy child-dal), NestedScrollView. Fragment navigációval rendelkező Activity létrehozásakor az Android Studio alapértelmezett sablonja a FrameLayout-ot (vagy FragmentContainerView-t) használja a fragmensek fő konténereként.
android:foreground — a FrameLayout attribútuma, amely egy drawable-t határoz meg, amely az összes gyermekelem fölött rajzolódik. A background-tól (a gyermekek alatt) eltérően a foreground a tartalom fölött jelenik meg, és lehet átlátszó. Használata: átfedési effektusok lenyomáskor (ripple ?attr/selectableItemBackground segítségével), képek maszkolása, állapotjelzők megjelenítése (kijelölő pipa a kép fölött).
A foreground támogatja a szabványos drawable erőforrásokat: ColorDrawable, ShapeDrawable, RippleDrawable, LayerDrawable. API 23+-tól elérhető az android:foregroundGravity a foreground pozicionálásához (fill, center, top, bottom). "Kitöltés" (fill) módban a foreground az egész FrameLayout-ra nyúlik; center módban a középpontba rajzolódik.
android:measureAllChildren — logikai attribútum (alapértelmezett true), amely meghatározza, hogy a FrameLayout méretének kiszámításakor az összes gyermekelem mérése szükséges-e. Ha true (alapértelmezett), a FrameLayout figyelembe veszi az összes gyermek méretét, beleértve a GONE (0 méretű) elemeket is. Ha false, a FrameLayout csak a VISIBLE és INVISIBLE gyermekeket méri — a GONE elemek kizárásra kerülnek a számításból. A Google I/O 2019 szerint a measureAllChildren kikapcsolása sok GONE elemet tartalmazó konténereknél (pl. láthatóságot váltó listák) 20–60%-kal gyorsítja a kezdeti renderelést.
android:layout_gravity — a FrameLayout (és más ViewGroup-ok) gyermekelemének attribútuma, amely meghatározza annak pozícióját a konténeren belül. A FrameLayoutban a layout_gravity az egyetlen mód a gyermekelem pozíciójának szabályozására, mivel a FrameLayout nem rendelkezik saját pozicionálási szabályokkal (mint a RelativeLayout) vagy iránnyal (mint a LinearLayout).
Lehetséges értékek: top, bottom, left, right, center, center_horizontal, center_vertical, fill, fill_horizontal, fill_vertical, clip_horizontal, clip_vertical. | jellel kombinálhatók: android:layout_gravity="bottom|center_horizontal" — az elem az alsó szélhez van nyomva és vízszintesen középre igazítva. Azon elemek esetében, amelyek mérete kisebb, mint a FrameLayout, a layout_gravity határozza meg a pozíciójukat a szabad helyen.
Ha a layout_gravity nincs beállítva, az elem alapértelmezés szerint a bal felső sarokba (top|left) kerül. Több gyermeket tartalmazó FrameLayout esetén mindegyiknek lehet saját layout_gravity-je — az egyik elem lehet a bal felső sarokban, a másik a jobb alsó sarokban, a harmadik a középpontban. Ez egyszerű átfedések létrehozását teszi lehetővé (pl. egy bezáró X jel a kép fölött).
android:gravity (szülő attribútum) a FrameLayouton belüli tartalmat igazítja — például a szöveget a TextView-n belül. android:layout_gravity (gyermek attribútum) magát az elemet igazítja a FrameLayouton belül. A FrameLayout kontextusában a gravity határozza meg, hogy a gyermekek alapértelmezés szerint hogyan helyezkedjenek el (a layout_gravity megfelelője az összes gyermekre egyszerre), de az egyes elemek layout_gravity-je felülírja a szülő értékét.
Kép és szöveges címke a jobb alsó sarokban. A FrameLayout egy teljes képernyős ImageView-t és egy layout_gravity="bottom|end" beállítású TextView-t tartalmaz a fölé pozicionáláshoz.
<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>
Az ImageView elfoglalja a teljes FrameLayout-ot (200dp magasság). A layout_gravity="bottom|end" beállítású TextView a kép fölött a jobb alsó sarokba kerül. Az elevation=2dp árnyékot ad a címke alá, vizuálisan elválasztva azt a képtől. Ez egy minimális példa, amely a LinearLayout-ban beágyazott konténert vagy egyedi kódot igényelne.
Képernyő tartalommal és egy középen lévő folyamatjelzővel, amely betöltéskor jelenik meg. A FrameLayout két elemet tartalmaz: a tartalmat és egy visibility="gone" beállítású ProgressBar-t (kódban kapcsolható).
<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="Betöltött tartalom"
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>
A ProgressBar alapértelmezés szerint rejtett (gone). A betöltés megkezdésekor a kódban meghívódik findViewById(R.id.loading_spinner).visibility = View.VISIBLE — a spinner megjelenik a középpontban a tartalom fölött. Betöltés után — .visibility = View.GONE. A FrameLayout biztosítja az átfedést a tartalom pozíciójának megváltoztatása nélkül — a szöveg nem mozdul el a spinner megjelenésekor, mivel a ProgressBar fölé rajzolódik.
FrameLayout szabványos konténerként a FragmentTransaction számára. Az aktivitás a navigációtól függően cseréli a fragmenseket ezen a konténeren belül.
<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 konténerként — a legkönnyebb módja a Fragment Navigation támogatásának. A FragmentContainerView (a FrameLayout leszármazottja) a Navigation Component 2.4+-ban ajánlott, de az egyszerű FrameLayout továbbra is érvényes a manuális FragmentTransaction-okhoz. A fő előny — a FragmentTransaction.replace() teljesen lecseréli a tartalmat anélkül, hogy befolyásolná az Activity-t.
A FrameLayout négy szcenárióhoz optimális: fragment konténer (FragmentContainerView vagy FrameLayout), betöltési átfedés (ProgressBar a tartalom fölött), kártyacímkék (jelölés a képen), helyőrző konténer ViewStub-hoz (lustán betöltés).
Nem alkalmas: több elem összetett pozicionálására (használjon ConstraintLayout-ot), szekvenciális listákhoz (LinearLayout vagy RecyclerView), dinamikus elrendezésekhez az elemek sorrendjének változásával.
A FrameLayout a képernyő gyökérelemeként — rossz gyakorlat, ha a képernyő több mint 2–3 elemet tartalmaz. Gyökér konténerként használja a ConstraintLayout-ot — több irányítást ad a pozicionálás fölött teljesítményveszteség nélkül. A FrameLayout-ot csak olyan gyermekblokkokhoz használja, ahol átfedésre vagy helyőrzőre van szükség.
Gyakran ismételt kérdések
A FrameLayout egy minimalista konténer pozicionálási rendszer nélkül (csak layout_gravity), elemek egymásra helyezésére tervezve. A ConstraintLayout egy teljes elrendezési rendszer kényszerekkel, láncokkal, korlátokkal, százalékos méretekkel és MotionLayout támogatással. A FrameLayout gyorsabb egyszerű esetekben (1 child vagy overlay); a ConstraintLayout kötelező az összetett elrendezésekhez. Választás: FrameLayout a betöltési átfedéshez és fragment konténerhez; ConstraintLayout minden máshoz.
Állítsa a FrameLayout méretét wrap_content-re a kívánt tengelyen (android:layout_width="wrap_content" és/vagy android:layout_height="wrap_content"). Ebben az esetben a FrameLayout a legnagyobb gyermekelem méretéhez igazodik. Ha az összes gyermek kisebb, mint a FrameLayout, a felesleges hely üresen marad. Pontos szabályozáshoz használjon match_parent-t fix padding-gal.
Ez a FrameLayout normál viselkedése — nem osztja el a gyermekeket a térben, hanem egymás után, egymás fölé rajzolja őket. Ha azt szeretné, hogy az elemek ne fedjék egymást, használjon másik konténert (LinearLayout, ConstraintLayout). Részleges átfedéshez a rajzolási sorrend szabályozásával használja a layout_gravity-t az eltoláshoz és az elevation-t a Z-order-hez.
A FrameLayout gyorsabb a maximálisan egyszerű esetekben — egy gyermekelem, összetett pozicionálás nélkül. A FrameLayout minimális idő alatt végrehajtja az onMeasure-t, számítás nélkül. A különbség azonban csak több száz ismétlésnél észlelhető (RecyclerView tízezernyi elemmel). Egy átlagos képernyőnél (1–3 FrameLayout) a különbség mikroszekundumokban mérhető, és nem befolyásolja az UX-t. A ConstraintLayout az univerzális választás a feladatok 95%-ához.
Összefoglalás
Kulcsrakész mobilalkalmazást fejlesztünk
Az IT Sectr 2017 óta készít iOS és Android alkalmazásokat induló vállalkozásoknak és vállalkozásoknak. Tanácsot adunk, és a legjobb megoldást javasoljuk.
Olvassa el is