בניית מעברים יפים עם תנועה מהותית ל-Android

1. מבוא

Material Design היא מערכת ליצירת מוצרים דיגיטליים נועזים ומרהיבים. כשמשלבים סגנון, מיתוג, אינטראקציה ותנועה במערך עקבי של עקרונות ורכיבים, צוותי המוצרים יכולים לממש את פוטנציאל העיצוב הגדול ביותר.

logo_components_color_2x_web_96dp.png

Material Components (MDC) עוזר למפתחים להטמיע Material Design. MDC נוצר על ידי צוות של מהנדסים ומעצבי חוויית המשתמש ב-Google, שכולל עשרות רכיבים יפים ופונקציונליים של ממשק המשתמש. זמין ל-Android, ל-iOS, לאינטרנט ול-Flutter.material.io/develop

מהי מערכת התנועה של Material ל-Android?

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

ארבעת הדפוסים העיקריים של מעבר Material הם:

  • טרנספורמציה של קונטיינר: מעבר בין רכיבי ממשק משתמש שכוללים קונטיינר; יוצרת חיבור גלוי בין שני רכיבים נפרדים בממשק המשתמש על ידי המרה חלקה של רכיב אחד לרכיב אחר.
  • ציר משותף: מעברים בין רכיבי ממשק משתמש שיש להם קשר מרחבי או ניווטי; משתמשת בטרנספורמציה משותפת על ציר ה-x, ה-y או ה-z כדי לחזק את הקשר בין יסודות.
  • עמעום: מעבר בין אלמנטים של ממשק משתמש שאין להם קשר חזק זה לזה; משתמשת באפקט הדרגתי ועמעום הדרגתי, עם קנה מידה של הרכיב הנכנס.
  • עמעום: משמש לרכיבים בממשק המשתמש שנכנסים לגבולות המסך או יוצאים מהם.

ספריית MDC-Android מציעה סיווגי מעבר לדפוסים האלה, שמבוססים על ספריית המעבר של AndroidX (androidx.transition) וגם על מסגרת המעבר של Android (android.transition):

AndroidX

  • זמין בחבילה של com.google.android.material.transition
  • תמיכה ברמת API 14 ומעלה
  • תומך ב-Fragments וב-Views, אבל לא בפעילויות או ב-Windows
  • מכיל תיקוני באגים ברקע והתנהגות עקבית ברמות ה-API

מסגרת

  • זמין בחבילה של com.google.android.material.transition.platform
  • תמיכה ברמת API ברמה 21 ואילך
  • תמיכה ב-Fragments, בתצוגות, בפעילויות וב-Windows
  • תיקוני באגים שלא הועברו לאחור ויכול להיות שאופן הפעולה שלהם יהיה שונה ברמות ה-API

ב-Codelab הזה תשתמשו במעברי Material שמבוססים על ספריית AndroidX, כלומר אתם תתמקדו בעיקר ב-Fragments וב-Views.

מה תפַתחו

ה-Codelab הזה ידריך אותך במעברים לאפליקציית אימייל לדוגמה ל-Android שנקראת Reply, באמצעות Kotlin, כדי להדגים איך אפשר להשתמש במעברים מספריית MDC-Android כדי להתאים אישית את העיצוב והסגנון של האפליקציה.

הקוד ההתחלתי של אפליקציית 'תשובה' יסופק, ואתם תשלבו את המעברים הבאים ב-Material באפליקציה. את המעברים הבאים אפשר לראות בקובץ ה-GIF שהושלם על ידי Codelab:

  • מעבר של Container Transform מרשימת כתובות האימייל לדף הפרטים של האימייל
  • מעבר של Container Transform מ-FAB לכתיבת דף אימייל
  • מעבר ציר ה-Z המשותף מסמל החיפוש לדף הצפייה בחיפוש
  • מעבר עמעום בין דפי תיבות דואר
  • מעבר Container Transform מצ'יפ של כתובת אימייל לתצוגת כרטיס

הדומיין של ה-iframe המבוקש (youtu.be) לא נוסף לרשימת ההיתרים.

מה צריך להכין

  • ידע בסיסי בפיתוח 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

  1. כשאשף ההגדרה יסתיים ובחלון ברוכים הבאים אל Android Studio, לוחצים על פתיחת פרויקט קיים של Android Studio.

e3f200327a67a53.png

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

6e026ae171f5b1eb.png

אימות יחסי התלות של פרויקטים

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

עוברים לקובץ build.gradle של המודול app ומוודאים שהבלוק dependencies כולל תלות ב-MDC-Android:

implementation 'com.google.android.material:material:1.2.0'

הפעלת אפליקציה לתחילת פעולה

  1. צריך לוודא שתצורת ה-build שמימין לאפשרות המכשיר היא app.
  2. לוחצים על לחצן ההפעלה / ההפעלה כדי לבנות את האפליקציה ולהפעיל אותה.

24218d0a6ae25803.png

  1. בחלון בחירת יעד פריסה, אם כבר יש לכם מכשיר Android שרשום ברשימת המכשירים הזמינים, מדלגים לשלב 8. אחרת, לוחצים על יצירת מכשיר וירטואלי חדש.
  2. במסך בחירת חומרה, בוחרים מכשיר טלפון, למשל Pixel 3, ולוחצים על הבא.
  3. במסך תמונת המערכת, בוחרים גרסת Android האחרונה, רצוי רמת ה-API הגבוהה ביותר. אם היא לא מותקנת, לוחצים על הקישור הורדה שמופיע ומשלימים את ההורדה.
  4. לוחצים על הבא.
  5. במסך מכשיר וירטואלי של Android (AVD), משאירים את ההגדרות כפי שהן ולוחצים על סיום.
  6. בוחרים מכשיר Android מתיבת הדו-שיח של יעד הפריסה.
  7. לוחצים על אישור.
  8. מערכת Android Studio יוצרת את האפליקציה, פורסת אותה ופותח אותה באופן אוטומטי במכשיר היעד.

הצלחת! קוד הסימן לתחילת הפעולה בדף הבית של התשובה צריך לפעול באמולטור שלכם. תיבת הדואר הנכנס אמורה להכיל רשימה של כתובות אימייל.

cc73eb0d0f779035.png

אופציונלי: האטה של האנימציות במכשיר

מאחר שה-Codelab הזה כולל מעברים מהירים אך מלוטשים, כדאי להאט את האנימציות במכשיר כדי לצפות בחלק מהפרטים המדויקים יותר של המעברים בזמן ההטמעה. ניתן לעשות זאת באמצעות פקודות מעטפת של adb או לחצן הגדרות מהירות. שימו לב שהשיטות האלה להאטת האנימציות במכשיר ישפיעו גם על האנימציות במכשיר מחוץ לאפליקציית 'תשובה'.

שיטה 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: לחצן 'הגדרות מהירות'

לחלופין, כדי להגדיר את לחצן ההגדרות המהירות, קודם צריך להפעיל את ההגדרה למפתחים במכשיר אם עדיין לא עשית זאת:

  1. פתיחת המכשיר 'הגדרות' יישום
  2. גוללים למטה לחלק התחתון ולוחצים על 'מידע על מכשיר האמולציה'
  3. גוללים למטה ולוחצים במהירות על 'מספר Build'. עד להפעלת הגדרות המפתח

לאחר מכן, צריך לבצע את הפעולות הבאות, עדיין בתוך ה'הגדרות' של המכשיר , כדי להפעיל את לחצן ההגדרות המהירות:

  1. לוחצים על סמל החיפוש או על סרגל החיפוש בחלק העליון של המסך.
  2. מקלידים 'אריחים'. בשדה החיפוש
  3. לוחצים על 'כרטיסי מידע למפתחים של הגדרות מהירות' שורה
  4. לוחצים על 'קנה המידה של אנימציית החלון' מתג

לסיום, במהלך ה-Codelab, מושכים כלפי מטה את לוח הודעות המערכת מהחלק העליון של המסך ומשתמשים בסמל c7e3f98200023f6a.png כדי להחליף בין אנימציות במהירות רגילה לאנימציות במהירות רגילה.

3. היכרות עם קוד האפליקציה לדוגמה

בואו נסתכל על הקוד. סיפקנו אפליקציה שמשתמשת בספרייה רכיב ניווט של Jetpack כדי לנווט בין כמה מקטעים שונים, והכול בפעילות אחת, MainActivity:

  • HomeFragment: הצגת רשימה של אימיילים
  • EmailFragment: מציג הודעת אימייל מלאה אחת
  • ComposeFragment: מאפשר ליצור הודעת אימייל חדשה
  • SearchFragment: הצגת תצוגה של חיפוש

קודם כול, כדי להבין איך מוגדר תרשים הניווט של האפליקציה, אפשר לפתוח את 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 ובארבעה מקטעים עיקריים כדי להגדיר מעברי Material שפועלים במקביל לפעולות הניווט השונות באפליקציה.

עכשיו, אחרי שקראתם את הקוד לתחילת העבודה, ניישם את המעבר הראשון.

4. הוספת מעבר של Container Transform מרשימת כתובות האימייל לדף הפרטים של האימייל

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

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

f0e8a92eb2216bce.gif

כדי להתחיל, צריך להוסיף מאפיין 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))
}

עכשיו אפשר לנסות להפעיל מחדש את האפליקציה.

ed62cedec31da268.gif

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

כדי לתקן את המעבר להחזרה, צריך להוסיף את שתי השורות הבאות ל-method onViewCreated ב-HomeFragment.kt:

HomeFragment.kt

postponeEnterTransition()
view.doOnPreDraw { startPostponedEnterTransition() }

כדאי לנסות להפעיל מחדש את האפליקציה. הקשה על מקש 'הקודם' אחרי פתיחת הודעת אימייל תכווץ את ההודעה בחזרה לרשימה. איזה יופי! כדאי להמשיך לשפר את האנימציה.

הבעיה של להיעלם רשימת כתובות האימייל היא כאשר בעת ניווט ל-Fragment חדש באמצעות רכיב הניווט, ה-Fragment הנוכחי מוסר מיד ומוחלף ב-Fragment החדש והנכנס. כדי שרשימת כתובות האימייל תישאר גלויה גם אחרי שמחליפים אותה, אפשר להוסיף מעבר ליציאה אל 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 יחול במסך הבית כולו, במקום על כל אחת מהתצוגות הנפרדות בהיררכיה, צריך לסמן את RecyclerView ב-fragment_home.xml כקבוצת מעבר.

fragment_home.xml

android:transitionGroup="true"

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

9df2b39d5a150418.gif

5. הוספת מעבר של Container Transform מ-FAB לכתיבת דף אימייל

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

d242c9708abd382c.gif

אנחנו משתמשים באותה מחלקה, אבל הדרך שבה אנחנו מגדירים את המופע הזה תהיה שונה כי ה-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 למסך הכתיבה שנראה כך:

81b68391ac4b0a9.gif

6. הוספת מעבר של ציר ה-Z המשותף מסמל החיפוש לדף תצוגת החיפוש

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

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

499e1a677b4216bb.gif

כדי להתחיל, צריך למצוא את ה-method navigateToSearch ב-MainActivity ולהוסיף את קטע הקוד הבא לפני הקריאה ל-method NavController navigate, כדי להגדיר יציאה מהמקטע הנוכחי ולהזין מחדש מעברים בציר ה-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, שמגדירה מעברים מסוג Enter ומחזיר 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 באופן עומק, וכך ליצור אפקט חלק בין שני המסכים.

e5c0b0a130e807db.gif

7. הוספת מעבר עמעום בין דפי תיבות דואר

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

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

2c874c0a4588e8fb.gif

כדי להתחיל, צריך למצוא את השיטה navigateToHome ב-MainActivity ולהוסיף את קטע הקוד הבא לפני הקריאה לשיטת NavController מסוג navigate, כדי להגדיר מעבר MaterialFadeThrough מהמקטע הנוכחי.

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()
}

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

f61dfd58ea7bd3fd.gif

8. הוספת מעבר של Container Transform מצ'יפ של כתובת אימייל לתצוגת כרטיס

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

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

6200c682da2382d5.gif

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

כדי להתחיל, פותחים את ComposeFragment ומוצאים את השיטה 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)

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

e823b28e2890e05d.gif

9. הפעולה הושלמה

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

454a47ba96017a25.gif

השלבים הבאים

למידע נוסף על מערכת התנועה Material, כדאי לעיין במפרט ובתיעוד המלא למפתחים, ולנסות להוסיף מעברי Material לאפליקציה!

תודה שניסית תנועה בעיצוב חדשני. אנחנו מקווים שנהניתם מה-Codelab הזה!

הצלחתי להשלים את ה-Codelab הזה תוך השקעה של זמן ומאמץ סבירים

נכון מאוד נכון נייטרלי לא נכון לא נכון בכלל

אני רוצה להמשיך להשתמש בעתיד במערכת התנועה מסוג Material

נכון מאוד נכון נייטרלי לא נכון לא נכון בכלל