Fragment API מספק שתי דרכים להשתמש באפקטים של תנועה ובטרנספורמציות
כדי לחבר באופן חזותי מקטעים במהלך הניווט. אחת מהאפשרויות האלה
Animation Framework, שמשתמשת בשתיהן
Animation
ו-
Animator
השנייה היא מסגרת המעבר, שכוללת
מעברים משותפים של רכיבים.
אפשר לציין אפקטים מותאמים אישית להזנת מקטעים וליציאה מהם, מעברים של אלמנטים משותפים בין מקטעים.
- אפקט Enter קובע את האופן שבו מקטע נכנס למסך. לדוגמה, אפשר ליצור אפקט כדי להחליק את המקטע כלפי מטה מהקצה של כשמנווטים אליו.
- אפקט יציאה קובע איך מקטע יוצא מהמסך. לדוגמה, אפשר ליצור אפקט עמעום הדרגתי של הפריים כשיוצאים ממנו ממנו.
- מעבר של רכיבים משותפים קובע את האופן שבו תצוגה מפורטת משותפת
שני מקטעים עובר ביניהם. לדוגמה, תמונה שמוצגת
ב
ImageView
במקטע A עובר למקטע B פעם B הופך לגלוי.
לראות אנימציות.
קודם כל, צריך ליצור אנימציות לאפקטים לכניסה וליציאה, להרצה בזמן הניווט למקטע חדש. אפשר להגדיר אנימציות בתור משאבי אנימציה למתחילים. המשאבים האלה מאפשרים לך להגדיר איך מקטעים צריכים להסתובב, למתוח, להפוך לעמעום, ולנוע במהלך האנימציה. לדוגמה, אפשר להוסיף את המקטע הנוכחי כך שהמקטע החדש ייעלם מהקצה השמאלי של המסך, כפי שמתואר באיור 1.
אפשר להגדיר את האנימציות האלה בספרייה res/anim
:
<!-- res/anim/fade_out.xml -->
<?xml version="1.0" encoding="utf-8"?>
<alpha xmlns:android="http://schemas.android.com/apk/res/android"
android:duration="@android:integer/config_shortAnimTime"
android:interpolator="@android:anim/decelerate_interpolator"
android:fromAlpha="1"
android:toAlpha="0" />
<!-- res/anim/slide_in.xml -->
<?xml version="1.0" encoding="utf-8"?>
<translate xmlns:android="http://schemas.android.com/apk/res/android"
android:duration="@android:integer/config_shortAnimTime"
android:interpolator="@android:anim/decelerate_interpolator"
android:fromXDelta="100%"
android:toXDelta="0%" />
אפשר גם להגדיר אנימציות לאפקטים של כניסה ויציאה שרצים.
בהקפצת הערימה החוזרת, מצב שיכול לקרות כשהמשתמש מקיש על הלחצן 'למעלה' או על
לחצן 'הקודם'. האנימציות האלה נקראות popEnter
ו-popExit
. עבור
למשל, כשמשתמש חוזר למסך קודם, כדאי אולי
את המקטע הנוכחי כדי להחליק מהקצה הימני של המסך
להפוך לשקוף בהדרגה.
ניתן להגדיר את האנימציות הבאות:
<!-- res/anim/slide_out.xml -->
<translate xmlns:android="http://schemas.android.com/apk/res/android"
android:duration="@android:integer/config_shortAnimTime"
android:interpolator="@android:anim/decelerate_interpolator"
android:fromXDelta="0%"
android:toXDelta="100%" />
<!-- res/anim/fade_in.xml -->
<alpha xmlns:android="http://schemas.android.com/apk/res/android"
android:duration="@android:integer/config_shortAnimTime"
android:interpolator="@android:anim/decelerate_interpolator"
android:fromAlpha="0"
android:toAlpha="1" />
אחרי שמגדירים את האנימציות, אפשר להפעיל אותן
FragmentTransaction.setCustomAnimations()
להעביר את משאבי האנימציה לפי מזהה המשאב שלהם, כפי שמוצג
בדוגמה הבאה:
Kotlin
supportFragmentManager.commit { setCustomAnimations( R.anim.slide_in, // enter R.anim.fade_out, // exit R.anim.fade_in, // popEnter R.anim.slide_out // popExit ) replace(R.id.fragment_container, fragment) addToBackStack(null) }
Java
Fragment fragment = new FragmentB(); getSupportFragmentManager().beginTransaction() .setCustomAnimations( R.anim.slide_in, // enter R.anim.fade_out, // exit R.anim.fade_in, // popEnter R.anim.slide_out // popExit ) .replace(R.id.fragment_container, fragment) .addToBackStack(null) .commit();
הגדרת מעברים
תוכלו גם להשתמש במעברים כדי להגדיר אפקטים של כניסה ויציאה. האלה אפשר להגדיר מעברים בקובצי משאבים בפורמט XML. לדוגמה, אפשר רוצה שהקטע הנוכחי ייעלם והקטע החדש יוחלף הקצה הימני של המסך. אפשר להגדיר את המעברים האלה כך:
<!-- res/transition/fade.xml -->
<fade xmlns:android="http://schemas.android.com/apk/res/android"
android:duration="@android:integer/config_shortAnimTime"/>
<!-- res/transition/slide_right.xml -->
<slide xmlns:android="http://schemas.android.com/apk/res/android"
android:duration="@android:integer/config_shortAnimTime"
android:slideEdge="right" />
לאחר הגדרת המעברים, ניתן להחיל אותם על ידי שליחת קריאה
setEnterTransition()
במקטע הנכנס,
setExitTransition()
בקטע שיוצא, מעבירים את משאבי המעבר המנופחים
לפי מזהה המשאב שלהם, כמו בדוגמה הבאה:
Kotlin
class FragmentA : Fragment() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) val inflater = TransitionInflater.from(requireContext()) exitTransition = inflater.inflateTransition(R.transition.fade) } } class FragmentB : Fragment() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) val inflater = TransitionInflater.from(requireContext()) enterTransition = inflater.inflateTransition(R.transition.slide_right) } }
Java
public class FragmentA extends Fragment { @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); TransitionInflater inflater = TransitionInflater.from(requireContext()); setExitTransition(inflater.inflateTransition(R.transition.fade)); } } public class FragmentB extends Fragment { @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); TransitionInflater inflater = TransitionInflater.from(requireContext()); setEnterTransition(inflater.inflateTransition(R.transition.slide_right)); } }
תמיכה ב-Fragments מעברים ל-AndroidX. למרות שיש תמיכה גם במקטעים מעברים בין מסגרות, מומלץ להשתמש במעברים של AndroidX, כי הם נתמכים ברמות API 14 והם מכילים תיקוני באגים שלא קיימים בגרסאות ישנות יותר של מעברים בין מסגרות.
שימוש במעברים משותפים של רכיבים
חלק ממסגרת המעבר,
מעברים בין רכיבים משותפים קובעים את האופן שבו התצוגות התואמות עוברות
שני מקטעים במהלך מעבר בין מקטעים. לדוגמה, ייתכן שתרצו
התמונה שמוצגת בImageView
בקטע A כדי לעבור למקטע B
ברגע ש-B הופך לגלוי, כפי שמוצג באיור 3.
ברמה הכללית, כך מבצעים מעבר של מקטעים באמצעות רכיבים משותפים:
- הקצאת שם מעבר ייחודי לכל תצוגת רכיב משותף.
- להוסיף תצוגות משותפות לרכיבים ולהעביר שמות
FragmentTransaction
- הגדרת אנימציה משותפת של מעבר רכיבים.
ראשית, יש להקצות שם מעבר ייחודי לכל תצוגת רכיבים משותפת
כדי לאפשר מיפוי של התצוגות ממקטע אחד למקטע הבא. הגדרה
את שם המעבר ברכיבים משותפים בכל פריסת מקטעים באמצעות
ViewCompat.setTransitionName()
שמספק תאימות לרמות API 14 ומעלה.
דוגמה, שם המעבר של ImageView
במקטעים A ו-B
אפשר להקצות את הערכים האלה:
Kotlin
class FragmentA : Fragment() { override fun onViewCreated(view: View, savedInstanceState: Bundle?) { ... val itemImageView = view.findViewById<ImageView>(R.id.item_image) ViewCompat.setTransitionName(itemImageView, “item_image”) } } class FragmentB : Fragment() { override fun onViewCreated(view: View, savedInstanceState: Bundle?) { ... val heroImageView = view.findViewById<ImageView>(R.id.hero_image) ViewCompat.setTransitionName(heroImageView, “hero_image”) } }
Java
public class FragmentA extends Fragment { @Override public void onViewCreated(@NonNull View view, Bundle savedInstanceState) { ... ImageView itemImageView = view.findViewById(R.id.item_image); ViewCompat.setTransitionName(itemImageView, “item_image”); } } public class FragmentB extends Fragment { @Override public void onViewCreated(@NonNull View view, Bundle savedInstanceState) { ... ImageView heroImageView = view.findViewById(R.id.hero_image); ViewCompat.setTransitionName(heroImageView, “hero_image”); } }
כדי לכלול את הרכיבים המשותפים במעבר המקטעים,
על FragmentTransaction
לדעת איך תצוגות התצוגות של כל רכיב משותף ממופות למאפיין אחד
מקטע לקטע הבא. הוסיפו כל אחד מהרכיבים המשותפים שלכם
FragmentTransaction
על ידי התקשרות
FragmentTransaction.addSharedElement()
,
מעבירים את התצוגה ואת שם המעבר של התצוגה המפורטת המתאימה
בקטע הבא, כמו בדוגמה הבאה:
Kotlin
val fragment = FragmentB() supportFragmentManager.commit { setCustomAnimations(...) addSharedElement(itemImageView, “hero_image”) replace(R.id.fragment_container, fragment) addToBackStack(null) }
Java
Fragment fragment = new FragmentB(); getSupportFragmentManager().beginTransaction() .setCustomAnimations(...) .addSharedElement(itemImageView, “hero_image”) .replace(R.id.fragment_container, fragment) .addToBackStack(null) .commit();
כדי לציין את אופן המעבר של הרכיבים המשותפים מקטע אחד לאחר,
יש להגדיר מעבר Enter בקטע להיות
עברת אל. שיחת טלפון
Fragment.setSharedElementEnterTransition()
בשיטה onCreate()
של המקטע, כפי שמוצג בדוגמה הבאה:
Kotlin
class FragmentB : Fragment() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) sharedElementEnterTransition = TransitionInflater.from(requireContext()) .inflateTransition(R.transition.shared_image) } }
Java
public class FragmentB extends Fragment { @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); Transition transition = TransitionInflater.from(requireContext()) .inflateTransition(R.transition.shared_image); setSharedElementEnterTransition(transition); } }
המעבר של shared_image
מוגדר כך:
<!-- res/transition/shared_image.xml -->
<transitionSet>
<changeImageTransform />
</transitionSet>
כל מחלקות המשנה של Transition
נתמכות כמעברים משותפים של רכיבים. אם המיקום
שאתם רוצים ליצור Transition
בהתאמה אישית,
יוצרים אנימציית מעבר בהתאמה אישית.
changeImageTransform
, שנעשה בו שימוש בדוגמה הקודמת, הוא אחד מהבאים
של התרגומים שנוצרו מראש, שניתן להשתמש בהם. אפשר למצוא עוד Transition
מחלקות המשנה בהפניה של ה-API עבור
כיתה Transition
.
כברירת מחדל, מעבר הכניסה לרכיב המשותף משמש גם בתור
מעבר return עבור רכיבים משותפים. מעבר ההחזרה קובע את האופן שבו
רכיבים משותפים חוזרים למקטע הקודם כאשר המקטע
"העסקה "קופצת" מהמקבץ האחורי. אם ברצונך לציין
של החזרת מוצרים קיימת, תוכלו לעשות זאת באמצעות
Fragment.setSharedElementReturnTransition()
בשיטה onCreate()
של המקטע.
תאימות חזויה בחזרה
ניתן להשתמש בתכונת חיזוי חזרה עם הרבה אנימציות, אבל לא בכולן. כשמטמיעים 'חיזוי חזרה', חשוב להביא בחשבון את השיקולים הבאים:
- ייבוא של
Transitions 1.5.0
ואילך אוFragments 1.7.0
ואילך. - המחלקה ומחלקות המשנה של
Animator
וספריית המעבר של AndroidX הם נתמך. - אין תמיכה במחלקה
Animation
ובספרייהTransition
של ה-framework. - אנימציות של מקטעים חזויים פועלות רק במכשירים עם Android 14 או גבוהה יותר.
setCustomAnimations
,setEnterTransition
,setExitTransition
setReenterTransition
,setReturnTransition
,setSharedElementEnterTransition
ו-setSharedElementReturnTransition
הם נתמך באמצעות חיזוי חזרה.
מידע נוסף זמין במאמר הבא: הוספת תמיכה באנימציות אחורה בזמן
דחיית מעברים
במקרים מסוימים, ייתכן שתצטרכו לדחות את מעבר המקטעים בשביל לפרק זמן קצר. לדוגמה, יכול להיות שתצטרכו להמתין עד שכל הצפיות יוצגו בקטע נכנס נמדדו והוצגו כך ש-Android יכולים לתעד במדויק את מצבי ההתחלה והסיום של המעבר.
בנוסף, ייתכן שהמעבר שלך יידחה עד לסיום הנתונים הנחוצים נטענו. לדוגמה, ייתכן שתצטרכו להמתין עד תמונות נטענו עבור רכיבים משותפים. אחרת, המעבר עשוי עלול להיות קשה אם הטעינה של תמונה הסתיימה במהלך המעבר או אחריו.
כדי לדחות מעבר, עליך לוודא תחילה שהמקטע
טרנזקציה מאפשרת סידור מחדש של שינויים במצב מקטע. כדי לאפשר סידור מחדש
שינויים במצב של מקטע, הפעלה
FragmentTransaction.setReorderingAllowed()
כפי שאפשר לראות בדוגמה הבאה:
Kotlin
val fragment = FragmentB() supportFragmentManager.commit { setReorderingAllowed(true) setCustomAnimation(...) addSharedElement(view, view.transitionName) replace(R.id.fragment_container, fragment) addToBackStack(null) }
Java
Fragment fragment = new FragmentB(); getSupportFragmentManager().beginTransaction() .setReorderingAllowed(true) .setCustomAnimations(...) .addSharedElement(view, view.getTransitionName()) .replace(R.id.fragment_container, fragment) .addToBackStack(null) .commit();
כדי לדחות את המעבר למעבר, צריך להתקשר
Fragment.postponeEnterTransition()
בשיטה onViewCreated()
של המקטע שהוזן:
Kotlin
class FragmentB : Fragment() { override fun onViewCreated(view: View, savedInstanceState: Bundle?) { ... postponeEnterTransition() } }
Java
public class FragmentB extends Fragment { @Override public void onViewCreated(@NonNull View view, Bundle savedInstanceState) { ... postponeEnterTransition(); } }
לאחר טעינת הנתונים ואתם מוכנים להתחיל במעבר,
Fragment.startPostponedEnterTransition()
הדוגמה הבאה משתמשת
החלקה בספרייה כדי לטעון תמונה
ל-ImageView
משותף, ודחיית המעבר המתאים עד שהתמונה
הטעינה הושלמה.
Kotlin
class FragmentB : Fragment() { override fun onViewCreated(view: View, savedInstanceState: Bundle?) { ... Glide.with(this) .load(url) .listener(object : RequestListener<Drawable> { override fun onLoadFailed(...): Boolean { startPostponedEnterTransition() return false } override fun onResourceReady(...): Boolean { startPostponedEnterTransition() return false } }) .into(headerImage) } }
Java
public class FragmentB extends Fragment { @Override public void onViewCreated(@NonNull View view, Bundle savedInstanceState) { ... Glide.with(this) .load(url) .listener(new RequestListener<Drawable>() { @Override public boolean onLoadFailed(...) { startPostponedEnterTransition(); return false; } @Override public boolean onResourceReady(...) { startPostponedEnterTransition(); return false; } }) .into(headerImage) } }
במקרים כמו החיבור האיטי של המשתמש לאינטרנט, ייתכן
צריכים שהמעבר שנדחה יתחיל לאחר פרק זמן מסוים
מאשר להמתין עד שכל הנתונים נטענים. במצבים כאלה, אפשר
להתקשר במקום זאת
Fragment.postponeEnterTransition(long, TimeUnit)
ב-method onViewCreated()
של המקטע, מעביר את משך הזמן
ואת יחידת הזמן. החיוב שנדחה יתחיל באופן אוטומטי ברגע
הזמן שצוין חלף.
שימוש במעברים משותפים של רכיבים עם RecyclerView
מעברי כניסה שנדחו לא אמורים להתחיל לפני כל הצפיות בכניסה
נמדדו ופרוסות. כשמשתמשים ב-
RecyclerView
, עליך להמתין
כדי שנתונים ייטענו וכדי ש-RecyclerView
הפריטים יהיו מוכנים לשרטוט
לפני התחלת המעבר. הנה דוגמה:
Kotlin
class FragmentA : Fragment() { override fun onViewCreated(view: View, savedInstanceState: Bundle?) { postponeEnterTransition() // Wait for the data to load viewModel.data.observe(viewLifecycleOwner) { // Set the data on the RecyclerView adapter adapter.setData(it) // Start the transition once all views have been // measured and laid out (view.parent as? ViewGroup)?.doOnPreDraw { startPostponedEnterTransition() } } } }
Java
public class FragmentA extends Fragment { @Override public void onViewCreated(@NonNull View view, Bundle savedInstanceState) { postponeEnterTransition(); final ViewGroup parentView = (ViewGroup) view.getParent(); // Wait for the data to load viewModel.getData() .observe(getViewLifecycleOwner(), new Observer<List<String>>() { @Override public void onChanged(List<String> list) { // Set the data on the RecyclerView adapter adapter.setData(it); // Start the transition once all views have been // measured and laid out parentView.getViewTreeObserver() .addOnPreDrawListener(new ViewTreeObserver.OnPreDrawListener() { @Override public boolean onPreDraw(){ parentView.getViewTreeObserver() .removeOnPreDrawListener(this); startPostponedEnterTransition(); return true; } }); } }); } }
שימו לב ש
ViewTreeObserver.OnPreDrawListener
מוגדר בהורה של תצוגת המקטעים. המטרה היא לוודא שכל
הצפיות של חלק מהמקטעים נמדדו ופרוסות, ולכן הן מוכנות
לפני תחילת המעבר של כניסה שנדחה.
נקודה נוספת שכדאי לחשוב עליה כשמשתמשים במעברים משותפים של רכיבים עם
RecyclerView
הוא שאי אפשר להגדיר את שם המעבר
פריסת XML של פריט אחד (RecyclerView
) בגלל מספר שרירותי של פריטים ששותפו
של הפריסה הזאת. יש להקצות שם העברה ייחודי כדי
אנימציית המעבר משתמשת בתצוגה הנכונה.
ניתן לתת לרכיב המשותף של כל פריט שם מעבר ייחודי באמצעות
מקצים אותן כשהערך של ViewHolder
מוגבל. לדוגמה, אם הנתונים של
כל פריט כולל מזהה ייחודי, שיכול לשמש כשם המעבר,
שמוצגת בדוגמה הבאה:
Kotlin
class ExampleViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) { val image = itemView.findViewById<ImageView>(R.id.item_image) fun bind(id: String) { ViewCompat.setTransitionName(image, id) ... } }
Java
public class ExampleViewHolder extends RecyclerView.ViewHolder { private final ImageView image; ExampleViewHolder(View itemView) { super(itemView); image = itemView.findViewById(R.id.item_image); } public void bind(String id) { ViewCompat.setTransitionName(image, id); ... } }
מקורות מידע נוספים
מידע נוסף על מעברי מקטעים זמין במקורות הנוספים הבאים: במשאבי אנוש.