FragmentManager
הוא
הכיתה שאחראית לביצוע הפעולות על מקטעי האפליקציה,
למשל להוסיף, להסיר או להחליף אותן ולהוסיף אותן למקבץ האחורי.
יכול להיות שלעולם לא תהיה לך אפשרות לבצע אינטראקציה ישירה עם FragmentManager
אם השתמשת
ספריית הניווט ב-Jetpack, מכיוון שהיא פועלת עם
FragmentManager
בשמך. עם זאת, כל אפליקציה שמשתמשת במקטעים היא
בשימוש ב-FragmentManager
ברמה מסוימת, לכן חשוב להבין
איך זה עובד, ואיך זה עובד.
הנושאים בדף הזה:
- איך לגשת אל
FragmentManager
. - התפקיד של
FragmentManager
ביחס לפעילויות ולמקטעים שלך. - איך לנהל את המקבץ האחורי באמצעות
FragmentManager
. - איך לספק נתונים ויחסי תלות למקטעים שלכם
גישה אל FragmentManager
אפשר לגשת אל FragmentManager
מתוך פעילות או מקטע.
FragmentActivity
ואת מחלקות המשנה שלו,
AppCompatActivity
,
יקבלו גישה אל FragmentManager
דרך
getSupportFragmentManager()
.
מקטעים יכולים לארח מקטע צאצא אחד או יותר. פנים החנות
מקטע, אפשר לקבל הפניה אל FragmentManager
שמנהל
בין הצאצאים של הקטע
getChildFragmentManager()
.
כדי לגשת למארח שלו, FragmentManager
, אפשר להשתמש ב-
getParentFragmentManager()
.
ריכזנו כאן כמה דוגמאות כדי להבין את הקשרים בין
מקטעים, המארחים שלהם והמופעים של FragmentManager
המשויכים
בכל אחת מהן.
איור 1 מציג שתי דוגמאות, שלכל אחת מהן יש מארח פעילות אחד.
פעילות המארח בשתי הדוגמאות האלה מציגה ניווט ברמה העליונה כדי
את המשתמש בתור
BottomNavigationView
שאחראי להחליף את המקטע של המארח במקטעים
במסכים באפליקציה. כל מסך מיושם כמקטע נפרד.
קטע המארח בדוגמה 1 מארח שני קטעי צאצא מסך מפוצל. קטע המארח בדוגמה 2 מארח שבר צאצא יחיד שמרכיבים את מקטע התצוגה של תצוגה מחליקים.
בגלל ההגדרה הזו, אפשר לחשוב על כל מארח שיש לו FragmentManager
שמשויכים אליו, שמנהלת את מקטעי הצאצא שלו. אפשר לראות את הדוגמה
תרשים 2 וגם מיפויי נכסים בין supportFragmentManager
,
parentFragmentManager
ו-childFragmentManager
.
מאפיין FragmentManager
המתאים שיש להפנות תלוי במיקום
ה-callsite נמצא בהיררכיית המקטעים שבה נמצא מנהל המקטעים
ניסית לגשת אליהם.
ברגע שתהיה לך הפניה אל FragmentManager
, אפשר להשתמש בו כדי
לערוך את המקטעים שמוצגים למשתמש.
שברי צאצא
באופן כללי, האפליקציה שלך מורכבת ממספר אחד או מספר קטן
של הפעילויות בפרויקט האפליקציה שלך, כשכל פעילות מייצגת
קבוצה של מסכים קשורים. הפעילות עשויה לספק נקודה למקום
ניווט ברמה העליונה ומקום להיקף אובייקטים של ViewModel
ומצבים אחרים של תצוגה
בין מקטעים. מקטע מייצג יעד יחיד ב-
אפליקציה.
כשרוצים להציג כמה מקטעים בבת אחת, למשל בתצוגה מפוצלת או במרכז שליטה, ניתן להשתמש במקטעי צאצא שמנוהלים על ידי של מקטע היעד ומנהל המקטעים הצאצא שלו.
עוד תרחישים לדוגמה של מקטעים צאצאים:
- מסך שקפים,
באמצעות
ViewPager2
במקטע הורה כדי לנהל סדרה של צאצאים תצוגות מפורטות. - ניווט משני בתוך קבוצה של מסכים קשורים.
- 'ניווט ב-Jetpack' משתמש במקטעים צאצאים כיעדים נפרדים.
הפעילות מארחת הורה יחיד
NavHostFragment
וממלאת את השטח שלו עם מקטעי צאצא שונים כאשר המשתמשים מנווטים באפליקציה שלך.
שימוש ב-FragmentManager
FragmentManager
מנהל את מקבץ המקטעים האחורי. בזמן הריצה,
FragmentManager
יכול לבצע פעולות גיבוי בסטאק, כמו הוספה או הסרה
מקטעים בתגובה לאינטראקציות של משתמשים. כל קבוצת שינויים
כיחידה אחת שנקראת
FragmentTransaction
לדיון מעמיק יותר על עסקאות במקטעים, כדאי לעיין
המדריך לטרנזקציות במקטעים.
כשהמשתמש מקיש על לחצן 'הקודם' במכשיר שלו, או בזמן שמתקשרים
FragmentManager.popBackStack()
טרנזקציית המקטע העליון ביותר קופצת מהמקבץ. אם אין עוד מקטעים
עסקאות במחסנית, ואם אתם לא משתמשים במקטעים צאצאים, הפונקציה
יופיעו בבועות של האירוע עד לפעילות. אם אתם משתמשים במקטעי צאצא, ראו:
שיקולים מיוחדים לגבי מקטעי של ילדים ואחים.
כשמתקשרים
addToBackStack()
בעסקה, העסקה יכולה לכלול כל מספר
פעולות כמו הוספת מקטעים מרובים או החלפת מקטעים
קונטיינרים.
כשהערימה האחורית "קופצת", כל
הפעולות הפוכות כפעולה אטומית יחידה. אבל אם התחייבתם
עסקאות נוספות לפני השיחה עם popBackStack()
, ואם
לא נעשה שימוש ב-addToBackStack()
לעסקה, הפעולות האלה
לא להפוך את הערכים. לכן, בתוך FragmentTransaction
יחיד, יש להימנע
שילוב של טרנזקציות שמשפיעות על הערימה הקודמת עם עסקאות שלא משפיעות על המקבץ.
ביצוע עסקה
כדי להציג מקטע בתוך קונטיינר פריסה, צריך להשתמש בפונקציה FragmentManager
כדי ליצור FragmentTransaction
. בתוך העסקה, תוכלו לאחר מכן
לבצע
add()
או replace()
במכל.
לדוגמה, FragmentTransaction
פשוט עשוי להיראות כך:
Kotlin
supportFragmentManager.commit { replace<ExampleFragment>(R.id.fragment_container) setReorderingAllowed(true) addToBackStack("name") // Name can be null }
Java
FragmentManager fragmentManager = getSupportFragmentManager(); fragmentManager.beginTransaction() .replace(R.id.fragment_container, ExampleFragment.class, null) .setReorderingAllowed(true) .addToBackStack("name") // Name can be null .commit();
בדוגמה זו, ExampleFragment
מחליף את המקטע, אם קיים,
במאגר הפריסה שזוהה על ידי
מזהה של R.id.fragment_container
. אספקת המחלקה של המקטע
replace()
מאפשרת ל-FragmentManager
לטפל ביצירה באמצעות
FragmentFactory
.
מידע נוסף זמין במאמר ציון יחסי תלות למקטעים
.
setReorderingAllowed(true)
מבצעת אופטימיזציה של שינויי המצב של המקטעים שמעורבים בעסקה
כדי שהאנימציות והמעברים יפעלו כמו שצריך. מידע נוסף על
לנווט באמצעות אנימציות ומעברים.
עסקאות מקטעים וגם
לנווט בין מקטעים באמצעות אנימציות.
ביצוע שיחה
addToBackStack()
שומרת את הטרנזקציה למקבץ האחורי. המשתמש יוכל לבטל את הפעולה בשלב מאוחר יותר.
עסקה ולהחזיר את הקטע הקודם באמצעות הקשה על הסמל 'הקודם'
לחצן. אם הוספתם או הסרתם מספר מקטעים בתוך
טרנזקציה, כל הפעולות האלה יבוטלו
קופץ. השם האופציונלי שצוין בקריאה addToBackStack()
מספק
באפשרותכם לחזור אל עסקה ספציפית באמצעות
popBackStack()
.
אם לא תתבצע התקשרות אל addToBackStack()
במהלך ביצוע עסקה
מסיר מקטע, ואז המקטע שהוסר מושמד כאשר
בעסקה מסוימת, והמשתמש לא יכול לחזור אליה. אם
קוראים לפונקציה addToBackStack()
כאשר מסירים מקטע, אז המקטע
רק STOPPED
ומאוחר יותר RESUMED
כשהמשתמש מנווט חזרה. התצוגה שלו
is מושמד במקרה הזה. מידע נוסף זמין במאמר הבא:
מחזור החיים של מקטע.
חיפוש מקטע קיים
אפשר לקבל הפניה למקטע הנוכחי בתוך מאגר פריסה
באמצעות
findFragmentById()
כדי לחפש מקטע לפי המזהה הנתון, צריך להשתמש ב-findFragmentById()
מנופח מ-XML או לפי מזהה מאגר תגים כשמוסיפים אותו
FragmentTransaction
. הנה דוגמה:
Kotlin
supportFragmentManager.commit { replace<ExampleFragment>(R.id.fragment_container) setReorderingAllowed(true) addToBackStack(null) } ... val fragment: ExampleFragment = supportFragmentManager.findFragmentById(R.id.fragment_container) as ExampleFragment
Java
FragmentManager fragmentManager = getSupportFragmentManager(); fragmentManager.beginTransaction() .replace(R.id.fragment_container, ExampleFragment.class, null) .setReorderingAllowed(true) .addToBackStack(null) .commit(); ... ExampleFragment fragment = (ExampleFragment) fragmentManager.findFragmentById(R.id.fragment_container);
לחלופין, אפשר להקצות תג ייחודי למקטע ולקבל
הפניה באמצעות
findFragmentByTag()
אפשר להקצות תג באמצעות מאפיין ה-XML מסוג android:tag
על מקטעים
מוגדרות בפריסה שלך או במהלך add()
או replace()
בתוך FragmentTransaction
.
Kotlin
supportFragmentManager.commit { replace<ExampleFragment>(R.id.fragment_container, "tag") setReorderingAllowed(true) addToBackStack(null) } ... val fragment: ExampleFragment = supportFragmentManager.findFragmentByTag("tag") as ExampleFragment
Java
FragmentManager fragmentManager = getSupportFragmentManager(); fragmentManager.beginTransaction() .replace(R.id.fragment_container, ExampleFragment.class, null, "tag") .setReorderingAllowed(true) .addToBackStack(null) .commit(); ... ExampleFragment fragment = (ExampleFragment) fragmentManager.findFragmentByTag("tag");
שיקולים מיוחדים בנוגע למקטעי של ילדים ואחים
רק FragmentManager
אחד יכול לשלוט במקבץ האחורי של המקטעים
בכל זמן נתון. אם לאפליקציה שלך מוצגים כמה מקטעים-אחים של האפליקציה
בו-זמנית, או אם האפליקציה שלך משתמשת במקטעים צאצאים, אז אחד
האפליקציה FragmentManager
מיועדת לטפל בניווט הראשי של האפליקציה.
כדי להגדיר את שבר הניווט הראשי בתוך טרנזקציה של מקטעים:
קוראים לפונקציה
setPrimaryNavigationFragment()
על הטרנזקציה, באמצעות העברה במופע של המקטע
ל-childFragmentManager
יש אמצעי בקרה ראשי.
נבחן את מבנה הניווט כסדרה של שכבות, יחד עם הפעילות בתור השכבה החיצונית ביותר, שעוטפת כל שכבה של מקטעים צאצא. לכל שכבה יש שבר ראשי יחיד של ניווט.
כאשר מאחור השכבה הפנימית ביותר שולטת בהתנהגות הניווט. אחרי ש השכבה הפנימית ביותר לא כוללת עוד עסקאות מקטעים שממנה אפשר לחזור, הבקרה תחזור לשכבה הבאה, והתהליך יחזור על עצמו עד להגיע לפעילות.
כאשר מוצגים שני מקטעים או יותר בו-זמנית, רק אחד מהם הוא מקטע הניווט הראשי. הגדרת מקטע מכיוון שמקטע הניווט הראשי מסיר את הסיווג ממקטע הניווט הקודם מקטע. בהמשך לדוגמה שלמעלה, אם הגדרתם את שבר הפרטים של מקטע הניווט הראשי, הסיווג של המקטע הראשי יוסר.
תמיכה בריבוי מקבצים
במקרים מסוימים, יכול להיות שהאפליקציה צריכה לתמוך בכמה מקבצים אחוריים.
למשל, אם האפליקציה משתמשת בסרגל ניווט תחתון. FragmentManager
מאפשר
קיימת תמיכה בכמה מערכי גיבויים חוזרים, עם saveBackStack()
restoreBackStack()
methods. השיטות האלה מאפשרות לעבור בין החלקים השונים
מקבצים על ידי שמירת ערימה קודמת אחת ושחזור מקבץ אחר.
saveBackStack()
פועלת באופן דומה להתקשרות אל popBackStack()
באמצעות
הפרמטר name
: העסקה שצוינה וכל העסקאות שאחריה
"ערימות" מפוצצות. ההבדל הוא ש-saveBackStack()
חוסך את
מצב של כל המקטעים
עסקאות.
לדוגמה, נניח שהוספתם בעבר מקטע למקבץ האחורי לפי
ביצוע FragmentTransaction
באמצעות addToBackStack()
, כמו שמוצג
בדוגמה הבאה:
Kotlin
supportFragmentManager.commit { replace<ExampleFragment>(R.id.fragment_container) setReorderingAllowed(true) addToBackStack("replacement") }
Java
supportFragmentManager.beginTransaction() .replace(R.id.fragment_container, ExampleFragment.class, null) // setReorderingAllowed(true) and the optional string argument for // addToBackStack() are both required if you want to use saveBackStack() .setReorderingAllowed(true) .addToBackStack("replacement") .commit();
במקרה הזה, ניתן לשמור את טרנזקציית המקטעים הזו ואת המצב של
ExampleFragment
בהתקשרות אל saveBackStack()
:
Kotlin
supportFragmentManager.saveBackStack("replacement")
Java
supportFragmentManager.saveBackStack("replacement");
אפשר לקרוא לפונקציה restoreBackStack()
עם אותו פרמטר שם כדי לשחזר את כל
העסקאות בחלון קופץ וכל המצבים של המקטע השמור:
Kotlin
supportFragmentManager.restoreBackStack("replacement")
Java
supportFragmentManager.restoreBackStack("replacement");
מספקים יחסי תלות למקטעים
כשמוסיפים מקטע, אפשר ליצור
את המקטע באופן ידני
להוסיף אותו לFragmentTransaction
.
Kotlin
fragmentManager.commit { // Instantiate a new instance before adding val myFragment = ExampleFragment() add(R.id.fragment_view_container, myFragment) setReorderingAllowed(true) }
Java
// Instantiate a new instance before adding ExampleFragment myFragment = new ExampleFragment(); fragmentManager.beginTransaction() .add(R.id.fragment_view_container, myFragment) .setReorderingAllowed(true) .commit();
כאשר מבצעים את טרנזקציית המקטעים, המופע של המקטע
שיצרתם הוא המכונה שבה נעשה שימוש. אבל, במהלך
שינוי בתצורה,
הפעילות וכל המקטעים שלה מושמדים ואז נוצרים מחדש
הרלוונטי ביותר
משאבי Android.
FragmentManager
מטפל בכל הדברים האלה בשבילך: הוא יוצר מחדש מופעים
של המקטעים, מצרף אותם למארח ויוצר מחדש את המקבץ האחורי
.
כברירת מחדל, FragmentManager
משתמש
FragmentFactory
ש
שה-framework מספק כדי ליצור מופע חדש של המקטע שלכם. הזה
במפעל ברירת מחדל משתמש בהשתקפות כדי למצוא ולהפעיל בנאי ללא ארגומנטים
למקטע שלך. המשמעות היא שלא ניתן להשתמש בהגדרות המקוריות שבברירת המחדל כדי
לספק יחסי תלות למקטע שלך. המשמעות היא גם שכל שינוי
שבו השתמשתם כדי ליצור את המקטע בפעם הראשונה לא נעשה שימוש
כברירת מחדל.
כדי לספק יחסי תלות למקטע שלך, או להשתמש בכל סוג של התאמה אישית
constructor, במקום זאת ליצור תת-מחלקה FragmentFactory
בהתאמה אישית
ואז לשנות מברירת המחדל
FragmentFactory.instantiate
.
לאחר מכן אפשר לשנות את הגדרות היצרן של FragmentManager
שמוגדרות כברירת מחדל באמצעות
במפעל מותאם אישית, שמשמש ליצירה של מקטעים.
נניח שיש לך DessertsFragment
שאחראי להצגה
וקינוחים פופולריים בעיר שלך, וגם DessertsFragment
תלוי במחלקה DessertsRepository
שמספקת לו
המידע שנחוץ לו כדי להציג למשתמש את ממשק המשתמש הנכון.
אפשר להגדיר שבDessertsFragment
יהיה צורך ב-DessertsRepository
ב-constructor שלו.
Kotlin
class DessertsFragment(val dessertsRepository: DessertsRepository) : Fragment() { ... }
Java
public class DessertsFragment extends Fragment { private DessertsRepository dessertsRepository; public DessertsFragment(DessertsRepository dessertsRepository) { super(); this.dessertsRepository = dessertsRepository; } // Getter omitted. ... }
הטמעה פשוטה של FragmentFactory
עשויה להיראות דומה לזה
את הדברים הבאים.
Kotlin
class MyFragmentFactory(val repository: DessertsRepository) : FragmentFactory() { override fun instantiate(classLoader: ClassLoader, className: String): Fragment = when (loadFragmentClass(classLoader, className)) { DessertsFragment::class.java -> DessertsFragment(repository) else -> super.instantiate(classLoader, className) } }
Java
public class MyFragmentFactory extends FragmentFactory { private DessertsRepository repository; public MyFragmentFactory(DessertsRepository repository) { super(); this.repository = repository; } @NonNull @Override public Fragment instantiate(@NonNull ClassLoader classLoader, @NonNull String className) { Class<? extends Fragment> fragmentClass = loadFragmentClass(classLoader, className); if (fragmentClass == DessertsFragment.class) { return new DessertsFragment(repository); } else { return super.instantiate(classLoader, className); } } }
בדוגמה הזו נעשה שימוש במחלקות FragmentFactory
, תוך שינוי המאפיין instantiate()
כדי לספק לוגיקה מותאמת אישית ליצירת מקטעים עבור DessertsFragment
.
מחלקות מקטעים אחרות מטופלים על ידי התנהגות ברירת המחדל של
FragmentFactory
עד super.instantiate()
.
לאחר מכן אפשר להגדיר את MyFragmentFactory
כמפעל לשימוש כאשר
לבנות את המקטעים של האפליקציה על ידי הגדרת מאפיין
FragmentManager
. עליך להגדיר את המאפיין הזה לפני הפעילות שלך
super.onCreate()
כדי לוודא שנעשה שימוש ב-MyFragmentFactory
כאשר
וליצור מחדש את המקטעים.
Kotlin
class MealActivity : AppCompatActivity() { override fun onCreate(savedInstanceState: Bundle?) { supportFragmentManager.fragmentFactory = MyFragmentFactory(DessertsRepository.getInstance()) super.onCreate(savedInstanceState) } }
Java
public class MealActivity extends AppCompatActivity { @Override protected void onCreate(@Nullable Bundle savedInstanceState) { DessertsRepository repository = DessertsRepository.getInstance(); getSupportFragmentManager().setFragmentFactory(new MyFragmentFactory(repository)); super.onCreate(savedInstanceState); } }
הגדרת ה-FragmentFactory
במקטע הפעילות מבטלת את המקטע
יצירה לכל אורך היררכיית המקטעים של הפעילות. במילים אחרות,
ה-childFragmentManager
של כל מקטעי הצאצא שמוסיפים משתמש במקטע המותאם אישית
היצרן של המקטעים הוגדר כאן, אלא אם הם בוטלו ברמה נמוכה יותר.
בדיקה באמצעות Fragment לייצור
בארכיטקטורת פעילות אחת, בדיקת המקטעים שלך
של בידוד באמצעות הפקודה
FragmentScenario
בכיתה. בגלל שאי אפשר להסתמך על הלוגיקה המותאמת אישית של onCreate
אפשר להעביר את FragmentFactory
כארגומנט
בבדיקת המקטעים שלך, כפי שמוצג בדוגמה הבאה:
// Inside your test val dessertRepository = mock(DessertsRepository::class.java) launchFragment<DessertsFragment>(factory = MyFragmentFactory(dessertRepository)).onFragment { // Test Fragment logic }
לקבלת מידע מפורט על תהליך הבדיקה ולעיון בדוגמאות מלאות, ראו בדיקת המקטעים.