1. מבוא
Material Design היא מערכת ליצירת מוצרים דיגיטליים מרשימים ויפים. צוותי המוצר יכולים לממש את פוטנציאל העיצוב הגדול ביותר שלהם על ידי איחוד של סגנון, מיתוג, אינטראקציה ותנועה תחת מערכת עקרונות ורכיבים עקבית.
רכיבי Material (MDC) עוזרים למפתחים להטמיע את Material Design. MDC נוצרה על ידי צוות של מהנדסים ומעצבי UX ב-Google. היא כוללת עשרות רכיבי ממשק משתמש יפים ופונקציונליים, וזמינה ל-Android, ל-iOS, לאינטרנט ול-Flutter.material.io/develop |
מהי מערכת התנועה של Material ל-Android?
מערכת התנועה של Material ל-Android היא קבוצה של דפוסי מעבר בספריית MDC-Android שיכולים לעזור למשתמשים להבין אפליקציה ולנווט בה, כפי שמתואר בהנחיות העיצוב של Material.
אלה ארבעת דפוסי המעבר העיקריים של Material:
- טרנספורמציה של קונטיינר: מעברים בין רכיבי ממשק משתמש שכוללים קונטיינר. יוצר חיבור גלוי בין שני רכיבי ממשק משתמש שונים על ידי טרנספורמציה חלקה של רכיב אחד לרכיב אחר.
- ציר משותף: מעברים בין רכיבי ממשק משתמש עם קשר מרחבי או קשרי ניווט. נעשה שימוש בטרנספורמציה משותפת בציר x, y או z כדי לשפר את הקשר בין הרכיבים.
- להפוך לשקוף בהדרגה: מעברים בין רכיבי ממשק משתמש שאין ביניהם קשר חזק. המעברים מתבצעים באמצעות הפיכה לשקוף בהדרגה של הרכיב היוצא והפיכה לשקוף בהדרגה של הרכיב הנכנס, עם שינוי גודל של הרכיב הנכנס.
- התעמעמות: משמשת לרכיבי ממשק משתמש שנכנסים ממסגרת גבולות המסך או יוצאים ממנה.
ספריית MDC-Android מציעה מחלקות מעבר לדפוסים האלה, שמבוססות על ספריית AndroidX Transition (androidx.transition) ועל Android Transition Framework (android.transition):
AndroidX
- זמין בחבילה
com.google.android.material.transition - תמיכה ברמת API 14 ומעלה
- תמיכה ב-Fragments וב-Views, אבל לא ב-Activities או ב-Windows
- כולל תיקוני באגים שהועברו מגרסאות חדשות יותר ותאימות בין רמות ה-API
Framework
- זמין בחבילה
com.google.android.material.transition.platform - תמיכה ברמת API 21 ומעלה
- תמיכה ב-Fragments, Views, Activities ו-Windows
- תיקוני באגים שלא הועברו לאחור, ויכול להיות שההתנהגות שלהם שונה ברמות שונות של API
ב-Codelab הזה תשתמשו במעברים של Material שנוצרו על בסיס ספריית AndroidX, כלומר תתמקדו בעיקר ב-Fragments וב-Views.
מה תפַתחו
ב-Codelab הזה נדגים איך משתמשים במעברים מהספרייה MDC-Android כדי להתאים אישית את המראה והתחושה של האפליקציה. לשם כך, נשתמש ב-Kotlin כדי להוסיף מעברים לאפליקציית אימייל לדוגמה ל-Android שנקראת Reply.
קוד לתחילת הדרך של אפליקציית Reply יסופק, ותצטרכו לשלב באפליקציה את המעברים הבאים של Material, שמוצגים ב-GIF של ה-codelab המלא שבהמשך:
- מעבר מטרנספורמציה של קונטיינר מרשימת כתובות אימייל לדף פרטי אימייל
- טרנספורמציה של קונטיינר: מעבר מלחצן ה-FAB לדף כתיבת אימייל
- מעבר בציר Z משותף מסמל החיפוש לדף תצוגת החיפוש
- החלפה הדרגתית בין דפים בתיבת הדואר
- Container Transform מעבר מצ'יפ של כתובת אימייל לתצוגת כרטיסי מיקום

מה תצטרכו
- ידע בסיסי בפיתוח ל-Android וב-Kotlin
- Android Studio (אם עדיין לא הורדתם אותו, אפשר להוריד אותו כאן)
- מכשיר או אמולטור Android (זמינים דרך Android Studio)
- קוד לדוגמה (ראו השלב הבא)
מה רמת הניסיון שלך בפיתוח אפליקציות ל-Android?
2. הגדרת סביבת הפיתוח
הפעלת Android Studio
כשפותחים את Android Studio, אמור להופיע חלון עם הכותרת 'ברוכים הבאים ל-Android Studio'. עם זאת, אם זו הפעם הראשונה שאתם מפעילים את Android Studio, אתם צריכים לבצע את השלבים של אשף ההגדרה של Android Studio עם ערכי ברירת המחדל. הורדה והתקנה של הקבצים הנדרשים בשלב הזה יכולות להימשך כמה דקות, לכן אפשר להשאיר את התהליך הזה ברקע ולעבור לקטע הבא.
אפשרות 1: שיבוט של אפליקציית ה-Codelab למתחילים מ-GitHub
כדי לשכפל את ה-codelab הזה מ-GitHub, מריצים את הפקודות הבאות:
git clone https://github.com/material-components/material-components-android-motion-codelab.git cd material-components-android-motion-codelab
אפשרות 2: הורדה של קובץ ה-ZIP של אפליקציית ה-Codelab למתחילים
אפליקציה לתחילת הדרך נמצאת בספרייה material-components-android-motion-codelab-develop.
טעינת קוד לתחילת הדרך ב-Android Studio
- אחרי שאשף ההגדרה יסיים את הפעולה ויוצג החלון Welcome to Android Studio (ברוכים הבאים ל-Android Studio), לוחצים על Open an existing Android Studio project (פתיחת פרויקט קיים של Android Studio).

- עוברים לספרייה שבה התקנתם את הקוד לדוגמה ובוחרים את הספרייה לדוגמה כדי לפתוח את הפרויקט.
- מחכים כמה רגעים עד ש-Android Studio יבצע build ויסנכרן את הפרויקט, כפי שמצוין על ידי אינדיקטורים של פעילות לאורך החלק התחתון של חלון Android Studio.
- בשלב הזה, יכול להיות ש-Android Studio יציג שגיאות build כי חסר לכם Android SDK או כלי build, כמו השגיאה שמוצגת למטה. פועלים לפי ההוראות ב-Android Studio כדי להתקין או לעדכן את התוספים האלה ולסנכרן את הפרויקט. אם אתם עדיין נתקלים בבעיות, כדאי לעיין במדריך לעדכון הכלים באמצעות SDK Manager.

אימות התלות של הפרויקט
הפרויקט צריך תלות ב-MDC-Android library. התלות הזו כבר אמורה להופיע בקוד לדוגמה שהורדתם, אבל כדאי לבדוק את ההגדרה כדי לוודא.
עוברים לקובץ build.gradle של מודול app ומוודאים שהבלוק dependencies כולל תלות ב-MDC-Android:
implementation 'com.google.android.material:material:1.2.0'
הפעלת אפליקציה לתחילת הדרך
- מוודאים שהגדרת ה-build בצד ימין של בחירת המכשיר היא
app. - לוחצים על הלחצן הירוק Run / Play כדי ליצור ולהריץ את האפליקציה.

- בחלון Select Deployment Target (בחירת יעד הפריסה), אם כבר מופיע מכשיר Android ברשימת המכשירים הזמינים, מדלגים אל שלב 8. אם לא, לוחצים על יצירת מכשיר וירטואלי חדש.
- במסך Select Hardware (בחירת חומרה), בוחרים מכשיר טלפון, כמו Pixel 3, ואז לוחצים על Next (הבא).
- במסך System Image, בוחרים גרסה עדכנית של Android, רצוי את רמת ה-API הגבוהה ביותר. אם היא לא מותקנת, לוחצים על הקישור הורדה שמוצג ומשלימים את ההורדה.
- לוחצים על הבא.
- במסך Android Virtual Device (AVD) (מכשיר וירטואלי של Android), משאירים את ההגדרות כמו שהן ולוחצים על Finish (סיום).
- בוחרים מכשיר Android מתיבת הדו-שיח של יעד הפריסה.
- לוחצים על אישור.
- Android Studio בונה את האפליקציה, פורס אותה ופותח אותה באופן אוטומטי במכשיר היעד.
הצלחת! קוד לתחילת הדרך של דף הבית של Reply צריך לפעול באמולטור. תיבת הדואר הנכנס אמורה להופיע עם רשימה של הודעות אימייל.

אופציונלי: האטה של האנימציות במכשיר
ב-codelab הזה יש מעברים מהירים אבל מלוטשים, ולכן כדאי להאט את האנימציות במכשיר כדי לראות חלק מהפרטים הקטנים של המעברים בזמן ההטמעה. אפשר לעשות את זה באמצעות פקודות shell או כפתור בהגדרות המהירות.adb חשוב לזכור שהשיטות האלה להאטת האנימציות במכשיר ישפיעו גם על אנימציות במכשיר מחוץ לאפליקציית Reply.
שיטה 1: פקודות ADB Shell
כדי להאט את האנימציות במכשיר פי 10, אפשר להריץ את הפקודות הבאות משורת הפקודה:
adb shell settings put global window_animation_scale 10
adb shell settings put global transition_animation_scale 10
adb shell settings put global animator_duration_scale 10
כדי לאפס את מהירות האנימציה במכשיר ולחזור למהירות הרגילה, מריצים את הפקודות הבאות:
adb shell settings put global window_animation_scale 1
adb shell settings put global transition_animation_scale 1
adb shell settings put global animator_duration_scale 1
שיטה 2: כפתור ב'הגדרות מהירות'
לחלופין, כדי להגדיר את הכפתור בהגדרות המהירות, צריך קודם להפעיל את הגדרות המפתחים במכשיר, אם לא עשיתם זאת בעבר:
- פותחים את אפליקציית ההגדרות במכשיר.
- גוללים לתחתית ולוחצים על 'מידע על מכשיר מדומה'.
- גוללים לתחתית ולוחצים במהירות על 'מספר build' עד שהאפשרות 'הגדרות למפתחים' מופעלת.
לאחר מכן, כדי להפעיל את כפתור ב"הגדרות מהירות", מבצעים את הפעולות הבאות באפליקציית ההגדרות של המכשיר:
- לוחצים על סמל החיפוש או על סרגל החיפוש בחלק העליון של המסך.
- מקלידים 'משבצות' בשדה החיפוש.
- לוחצים על השורה 'כפתורים למפתחים בהגדרות המהירות'.
- לוחצים על המתג 'מהירות ההפעלה של אנימציה של חלון'.
לבסוף, במהלך ה-codelab, מחליקים למטה מהחלק העליון של המסך כדי לפתוח את מרכז ההתראות של המערכת ומשתמשים בסמל
כדי לעבור בין אנימציות במהירות איטית לבין אנימציות במהירות רגילה.
3. הסבר על קוד האפליקציה לדוגמה
בואו נסתכל על הקוד. סיפקנו אפליקציה שמשתמשת בספריית רכיב הניווט של Jetpack כדי לנווט בין כמה מקטעים (fragments) שונים, כולם בתוך פעילות אחת, MainActivity:
- HomeFragment: הצגת רשימה של הודעות אימייל
- EmailFragment: הצגת אימייל מלא אחד
- ComposeFragment: מאפשר לכתוב אימייל חדש
- SearchFragment: הצגת תצוגת חיפוש
navigation_graph.xml
קודם כל, כדי להבין איך מוגדר גרף הניווט של האפליקציה, פותחים את navigation_graph.xml בספרייה app -> src -> main -> res -> navigation:
<navigation xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="@+id/navigation_graph"
app:startDestination="@id/homeFragment">
<fragment
android:id="@+id/homeFragment"
android:name="com.materialstudies.reply.ui.home.HomeFragment"
android:label="HomeFragment">
<argument...>
<action
android:id="@+id/action_homeFragment_to_emailFragment"
app:destination="@id/emailFragment" />
</fragment>
<fragment
android:id="@+id/emailFragment"
android:name="com.materialstudies.reply.ui.email.EmailFragment"
android:label="EmailFragment">
<argument...>
</fragment>
<fragment
android:id="@+id/composeFragment"
android:name="com.materialstudies.reply.ui.compose.ComposeFragment"
android:label="ComposeFragment">
<argument...>
</fragment>
<fragment
android:id="@+id/searchFragment"
android:name="com.materialstudies.reply.ui.search.SearchFragment"
android:label="SearchFragment" />
<action
android:id="@+id/action_global_homeFragment"
app:destination="@+id/homeFragment"
app:launchSingleTop="true"
app:popUpTo="@+id/navigation_graph"
app:popUpToInclusive="true"/>
<action
android:id="@+id/action_global_composeFragment"
app:destination="@+id/composeFragment" />
<action
android:id="@+id/action_global_searchFragment"
app:destination="@+id/searchFragment" />
</navigation>
שימו לב שכל המקטעים שצוינו למעלה מופיעים, ומקטע ההפעלה שמוגדר כברירת מחדל הוא HomeFragment דרך app:startDestination="@id/homeFragment". הגדרת ה-XML הזו של גרף היעד של הפרגמנט, וגם הפעולות, מספקת מידע לקוד הניווט של Kotlin שנוצר, שבו תיתקלו כשמחברים מעברים.
activity_main.xml
בשלב הבא, בודקים את הפריסה של activity_main.xml במאגר app -> src -> main -> res -> layout. יוצג NavHostFragment שהוגדר עם גרף הניווט שלמעלה:
<fragment
android:id="@+id/nav_host_fragment"
android:name="androidx.navigation.fragment.NavHostFragment"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:defaultNavHost="true"
app:navGraph="@navigation/navigation_graph"/>
ה-NavHostFragment הזה ממלא את המסך ומטפל בכל השינויים בניווט של מקטע המסך המלא באפליקציה. ה-BottomAppBar וה-FloatingActionButton המעוגן שלו, שגם הוא נמצא ב-activity_main.xml, מוצבים מעל המקטע הנוכחי שמוצג על ידי ה-NavHostFragment, ולכן יוצגו או יוסתרו בהתאם ליעד של המקטע לפי קוד האפליקציה לדוגמה שסופק.
בנוסף, BottomNavDrawerFragment ב-activity_main.xml הוא חלונית הזזה תחתונה שמכילה תפריט לניווט בין תיבות דואר שונות, שמוצג באופן מותנה באמצעות לחצן הלוגו BottomAppBar 'תשובה'.
MainActivity.kt
לבסוף, כדי לראות דוגמה לשימוש בפעולת ניווט, פותחים את MainActivity.kt בספרייה app -> src -> main -> java -> com.materialstudies.reply.ui. מחפשים את הפונקציה navigateToSearch(), שצריכה להיראות כך:
private fun navigateToSearch() {
val directions = SearchFragmentDirections.actionGlobalSearchFragment()
findNavController(R.id.nav_host_fragment).navigate(directions)
}
הסרטון הזה מראה איך אפשר לנווט לדף תצוגת החיפוש, בלי מעבר מותאם אישית. ב-Codelab הזה תצללו ל-MainActivity ובארבעה מקטעים (fragments) עיקריים של Reply כדי להגדיר מעברים של Material שפועלים בשילוב עם פעולות הניווט השונות באפליקציה.
עכשיו, אחרי שהכרתם את קוד לתחילת הדרך, נטמיע את המעבר הראשון.
4. הוספת מעבר של Container Transform מרשימת אימיילים לדף פרטי האימייל
כדי להתחיל, מוסיפים מעבר כשלוחצים על אימייל. לשינוי הניווט הזה מתאים דפוס הטרנספורמציה של הקונטיינר, כי הוא מיועד למעברים בין רכיבי ממשק משתמש שמכילים קונטיינר. הדפוס הזה יוצר חיבור גלוי בין שני רכיבי ממשק משתמש.
לפני שמוסיפים קוד, מנסים להריץ את אפליקציית Reply ולוחצים על אימייל. הוא צריך לבצע חיתוך פשוט, כלומר המסך מוחלף ללא מעבר:

מתחילים בהוספת מאפיין transitionName בתג MaterialCardView ב-email_item_layout.xml, כמו שמוצג בקטע הקוד הבא:
email_item_layout.xml
android:transitionName="@{@string/email_card_transition_name(email.id)}"
שם המעבר מקבל משאב מחרוזת עם פרמטר. צריך להשתמש במזהה של כל כתובת אימייל כדי לוודא שכל transitionName בEmailFragment שלנו הוא ייחודי.
אחרי שקבעתם את שם המעבר של הפריט ברשימת כתובות האימייל, צריך לעשות את אותו הדבר בפריסת פרטי האימייל. ב-fragment_email.xml, מגדירים את transitionName של MaterialCardView למשאב המחרוזת הבא:
fragment_email.xml
android:transitionName="@string/email_card_detail_transition_name"
ב-HomeFragment.kt, מחליפים את הקוד ב-onEmailClicked בקטע הקוד שבהמשך כדי ליצור את המיפוי מתצוגת ההתחלה (פריט ברשימת כתובות האימייל) לתצוגת הסיום (מסך פרטי האימייל):
HomeFragment.kt
val emailCardDetailTransitionName = getString(R.string.email_card_detail_transition_name)
val extras = FragmentNavigatorExtras(cardView to emailCardDetailTransitionName)
val directions = HomeFragmentDirections.actionHomeFragmentToEmailFragment(email.id)
findNavController().navigate(directions, extras)
אחרי שמגדירים את הצינורות, אפשר ליצור טרנספורמציה של מאגר תגים. בשיטה EmailFragment onCreate, מגדירים את sharedElementEnterTransition למופע חדש של MaterialContainerTransform (ייבוא של גרסה com.google.android.material.transition ולא של גרסה com.google.android.material.transition.platform) על ידי הוספת קטע הקוד הבא:
EmailFragment.kt
sharedElementEnterTransition = MaterialContainerTransform().apply {
drawingViewId = R.id.nav_host_fragment
duration = resources.getInteger(R.integer.reply_motion_duration_large).toLong()
scrimColor = Color.TRANSPARENT
setAllContainerColors(requireContext().themeColor(R.attr.colorSurface))
}
עכשיו מנסים להפעיל מחדש את האפליקציה.

הכול נראה מצוין! כשלוחצים על אימייל ברשימת כתובות האימייל, רכיב ה-container transform אמור להרחיב את הפריט ברשימה לדף פרטים במסך מלא. אבל שימו לב שאם תלחצו על 'הקודם', האימייל לא יצטמצם בחזרה לרשימה. בנוסף, רשימת כתובות האימייל נעלמת מיד בתחילת המעבר, ומוצג הרקע האפור של החלון. אז עוד לא סיימנו.
כדי לתקן את המעבר להחזרה, מוסיפים את שתי השורות הבאות לשיטה onViewCreated ב-HomeFragment.kt:
HomeFragment.kt
postponeEnterTransition()
view.doOnPreDraw { startPostponedEnterTransition() }
נסו להפעיל מחדש את האפליקציה. אם תלחצו על 'הקודם' אחרי פתיחת אימייל, האימייל יצטמצם בחזרה לרשימה. איזה יופי! בואו נמשיך לשפר את האנימציה.
הבעיה של היעלמות רשימת כתובות האימייל נובעת מכך שכאשר עוברים למקטע חדש באמצעות רכיב הניווט, המקטע הנוכחי מוסר מיד ומוחלף במקטע החדש שנכנס. כדי שרשימת כתובות האימייל תישאר גלויה גם אחרי שהיא תוחלף, אפשר להוסיף מעבר יציאה ל-HomeFragment.
כדי שהרשימה של האימיילים תתרחב בצורה עדינה כשיוצאים ממנה ותחזור למצב הקודם כשנכנסים אליה שוב, מוסיפים את קטע הקוד שלמטה לשיטה HomeFragment onEmailClicked:
HomeFragment.kt
exitTransition = MaterialElevationScale(false).apply {
duration = resources.getInteger(R.integer.reply_motion_duration_large).toLong()
}
reenterTransition = MaterialElevationScale(true).apply {
duration = resources.getInteger(R.integer.reply_motion_duration_large).toLong()
}
כדי לוודא שהמעבר MaterialElevationScale יחול על מסך הבית כולו ולא על כל אחת מהתצוגות הנפרדות בהיררכיה, מסמנים את MaterialElevationScale ב-fragment_home.xml כקבוצת מעברים.RecyclerView
fragment_home.xml
android:transitionGroup="true"
בשלב הזה, אמור להיות לכם טרנספורמציה של קונטיינר שפועלת באופן מלא. לחיצה על אימייל מרחיבה את הפריט ברשימה למסך פרטים, והרשימה של האימיילים נסוגה. כשלוחצים על 'הקודם', מסך פרטי האימייל מצטמצם חזרה לפריט ברשימה, ומתרחב ברשימת האימיילים.

5. הוספת מעבר של טרנספורמציה של קונטיינר מלחצן ה-FAB לדף כתיבת האימייל
נמשיך עם שינוי הצורה של הקונטיינר ונוסיף מעבר מלחצן הפעולה הצף (FAB) אל ComposeFragment, כך שלחצן הפעולה הצף יתרחב לאימייל חדש שהמשתמש יכתוב. קודם מריצים מחדש את האפליקציה ולוחצים על ה-FAB כדי לראות שאין מעבר כשמפעילים את מסך כתיבת האימייל.

אנחנו משתמשים באותה מחלקת מעברים, אבל אופן ההגדרה של המופע הזה יהיה שונה כי ה-FAB שלנו נמצא ב-MainActivity וה-ComposeFragment שלנו מוצב בתוך מארח הניווט MainActivity.
ב-ComposeFragment.kt, מוסיפים את קטע הקוד הבא לשיטה onViewCreated, ומוודאים שמייבאים את גרסת androidx.transition של Slide.
ComposeFragment.kt
enterTransition = MaterialContainerTransform().apply {
startView = requireActivity().findViewById(R.id.fab)
endView = emailCardView
duration = resources.getInteger(R.integer.reply_motion_duration_large).toLong()
scrimColor = Color.TRANSPARENT
containerColor = requireContext().themeColor(R.attr.colorSurface)
startContainerColor = requireContext().themeColor(R.attr.colorSecondary)
endContainerColor = requireContext().themeColor(R.attr.colorSurface)
}
returnTransition = Slide().apply {
duration = resources.getInteger(R.integer.reply_motion_duration_medium).toLong()
addTarget(R.id.email_card_view)
}
בנוסף לפרמטרים שמשמשים להגדרת טרנספורמציית מאגר התגים הקודם, הפרמטרים startView ו-endView מוגדרים כאן באופן ידני. במקום להשתמש במאפייני transitionName כדי להודיע למערכת המעברים של Android אילו תצוגות צריך לשנות, אפשר לציין את התצוגות האלה באופן ידני כשצריך.
עכשיו מריצים מחדש את האפליקציה. לחצן ה-FAB אמור להפוך למסך הכתיבה (ראו את קובץ ה-GIF בסוף השלב הזה).
בדומה לשלב הקודם, צריך להוסיף מעבר ל-HomeFragment כדי שהרכיב לא ייעלם אחרי ההסרה וההחלפה ב-ComposeFragment.
מעתיקים את קטע הקוד שלמטה ל-method navigateToCompose ב-MainActivity לפני הקריאה NavController navigate.
MainActivity.kt
currentNavigationFragment?.apply {
exitTransition = MaterialElevationScale(false).apply {
duration = resources.getInteger(R.integer.reply_motion_duration_large).toLong()
}
reenterTransition = MaterialElevationScale(true).apply {
duration = resources.getInteger(R.integer.reply_motion_duration_large).toLong()
}
}
סיימתם את השלב הזה! המעבר מלחצן ה-FAB למסך הכתיבה צריך להיראות כך:

6. הוספת מעבר משותף בציר Z מסמל החיפוש לדף תצוגת החיפוש
בשלב הזה, נוסיף מעבר מסמל החיפוש לתצוגת החיפוש במסך מלא. מכיוון שאין מיכל מתמשך שמעורב בשינוי הניווט הזה, אפשר להשתמש במעבר משותף בציר Z כדי לחזק את הקשר המרחבי בין שני המסכים ולציין מעבר ברמה אחת למעלה בהיררכיה של האפליקציה.
לפני שמוסיפים קוד נוסף, מנסים להריץ את האפליקציה ומקישים על סמל החיפוש בפינה השמאלית התחתונה של המסך. הפעולה הזו אמורה להציג את מסך תצוגת החיפוש ללא מעבר.

כדי להתחיל, מחפשים את השיטה navigateToSearch ב-MainActivity ומוסיפים את קטע הקוד הבא לפני הפעלת method NavController navigate כדי להגדיר את המעברים הנוכחיים של מקטע (fragment) היוצא והנכנס בציר Z של MaterialSharedAxis.
MainActivity.kt
currentNavigationFragment?.apply {
exitTransition = MaterialSharedAxis(MaterialSharedAxis.Z, true).apply {
duration = resources.getInteger(R.integer.reply_motion_duration_large).toLong()
}
reenterTransition = MaterialSharedAxis(MaterialSharedAxis.Z, false).apply {
duration = resources.getInteger(R.integer.reply_motion_duration_large).toLong()
}
}
לאחר מכן, מוסיפים את קטע הקוד הבא לשיטה onCreate ב-SearchFragment, שמגדירה את המעברים של הכניסה והחזרה MaterialSharedAxis.
SearchFragment.kt
enterTransition = MaterialSharedAxis(MaterialSharedAxis.Z, true).apply {
duration = resources.getInteger(R.integer.reply_motion_duration_large).toLong()
}
returnTransition = MaterialSharedAxis(MaterialSharedAxis.Z, false).apply {
duration = resources.getInteger(R.integer.reply_motion_duration_large).toLong()
}
לבסוף, כדי לוודא שהמעבר של MaterialSharedAxis יחול על מסך החיפוש כולו, ולא על כל אחת מהתצוגות הנפרדות בהיררכיה, צריך לסמן את LinearLayout ב-fragment_search.xml כקבוצת מעברים.
fragment_search.xml
android:transitionGroup="true"
זהו! עכשיו מנסים להפעיל מחדש את האפליקציה ומקישים על סמל החיפוש. מסכי הבית ותצוגת החיפוש צריכים לדהות ולשנות את הגודל שלהם בו-זמנית לאורך ציר Z בעומק, כדי ליצור מעבר חלק בין שני המסכים.

7. הוספת מעבר של דהייה בין דפי תיבת הדואר
בשלב הזה נוסיף מעבר בין תיבות דואר שונות. אנחנו לא רוצים להדגיש קשר מרחבי או היררכי, ולכן נשתמש במעבר הדרגתי כדי לבצע 'החלפה' פשוטה בין רשימות של אימיילים.
לפני שמוסיפים קוד נוסף, מנסים להפעיל את האפליקציה, מקישים על הלוגו של התשובה בסרגל האפליקציות התחתון ומחליפים תיבות דואר. רשימת האימיילים צריכה להשתנות ללא מעבר.

כדי להתחיל, מחפשים את השיטה navigateToHome ב-MainActivity ומוסיפים את קטע הקוד הבא לפני הפעלת method NavController navigate כדי להגדיר את מעבר היציאה MaterialFadeThrough של המקטע (fragment) הנוכחי.
MainActivity.kt
currentNavigationFragment?.apply {
exitTransition = MaterialFadeThrough().apply {
duration = resources.getInteger(R.integer.reply_motion_duration_large).toLong()
}
}
לאחר מכן, פותחים את HomeFragment. ב-onCreate, מגדירים את enterTransition של הפריט למופע חדש של MaterialFadeThrough.
HomeFragment.kt
enterTransition = MaterialFadeThrough().apply {
duration = resources.getInteger(R.integer.reply_motion_duration_large).toLong()
}
מריצים מחדש את האפליקציה. כשפותחים את חלונית ההזזה לניווט התחתונה ומחליפים תיבות דואר, הרשימה הנוכחית של האימיילים אמורה לדהות ולהתרחק, והרשימה החדשה אמורה לדהות ולהתקרב. איזה יופי!

8. הוספת מעבר של Container Transform מצ'יפ של כתובת אימייל לתצוגת כרטיסי מיקום
בשלב הזה תוסיפו מעבר שיהפוך צ'יפ לכרטיס קופץ. השימוש בטרנספורמציה של מאגר התגים כאן נועד ליידע את המשתמש שהפעולה שבוצעה בחלון הקופץ תשפיע על הצ'יפ שממנו נפתח החלון הקופץ.
לפני שמוסיפים קוד, מפעילים את אפליקציית Reply, לוחצים על אימייל, לוחצים על לחצן הפעולה הצף 'תשובה' ואז מנסים ללחוץ על צ'יפ של איש קשר של נמען. הצ'יפ אמור להיעלם באופן מיידי, וכרטיס עם כתובות האימייל של איש הקשר אמור להופיע ללא אנימציות.

תצטרכו לעבוד ב-ComposeFragment בשלב הזה. פריסת ComposeFragment כוללת כבר צ'יפים של נמענים (גלויים כברירת מחדל) וכרטיס נמען (לא גלוי כברירת מחדל). הצ'יפ של הנמען והכרטיס הזה הם שתי התצוגות שביניהן תיצרו טרנספורמציה של קונטיינר.
כדי להתחיל, פותחים את ComposeFragment ומחפשים את ה-method expandChip. השיטה הזו מופעלת כשלוחצים על chip שצוין. מוסיפים את קטע הקוד הבא מעל השורות שמחליפות את הגדרות החשיפה של recipientCardView ו-chip, מה שיפעיל את שינוי המאגר שנרשם באמצעות beginDelayedTransition.
ComposeFragment.kt
val transform = MaterialContainerTransform().apply {
startView = chip
endView = binding.recipientCardView
scrimColor = Color.TRANSPARENT
endElevation = requireContext().resources.getDimension(
R.dimen.email_recipient_card_popup_elevation_compat
)
addTarget(binding.recipientCardView)
}
TransitionManager.beginDelayedTransition(binding.composeConstraintLayout, transform)
אם מריצים את האפליקציה עכשיו, הצ'יפ אמור להפוך לכרטיס של כתובות אימייל של הנמען. בשלב הבא, נגדיר את מעבר החזרה כדי לכווץ את הכרטיס בחזרה אל הצ'יפ.
בשיטה collapseChip ב-ComposeFragment, מוסיפים את קטע הקוד הבא כדי לכווץ את הכרטיס בחזרה לצ'יפ.
ComposeFragment.kt
val transform = MaterialContainerTransform().apply {
startView = binding.recipientCardView
endView = chip
scrimColor = Color.TRANSPARENT
startElevation = requireContext().resources.getDimension(
R.dimen.email_recipient_card_popup_elevation_compat
)
addTarget(chip)
}
TransitionManager.beginDelayedTransition(binding.composeConstraintLayout, transform)
מריצים מחדש את האפליקציה. לחיצה על הצ'יפ אמורה להרחיב אותו לכרטיס, ולחיצה על הכרטיס אמורה לכווץ אותו בחזרה לצ'יפ. איזה יופי!

9. הפעולה הושלמה
בעזרת פחות מ-100 שורות של קוד Kotlin וכמה תגי XML בסיסיים, ספריית MDC-Android עזרה לכם ליצור מעברים יפים באפליקציה קיימת שתואמת להנחיות של Material Design, וגם נראית ומתנהגת באופן עקבי בכל מכשירי Android.

השלבים הבאים
כדי לקבל מידע נוסף על מערכת התנועה של Material, כדאי לעיין במפרט ובמסמכי העזרה המלאים למפתחים ולנסות להוסיף לאפליקציה כמה מעברים של Material.
תודה שניסית את Material motion. אנחנו מקווים שנהניתם מה-Codelab הזה.