การสร้างการเปลี่ยนภาพที่สวยงามด้วย Material Motion สำหรับ Android

1. บทนำ

Material Design คือระบบสำหรับการสร้างผลิตภัณฑ์ดิจิทัลที่โดดเด่นและสวยงาม การรวมสไตล์ การสร้างแบรนด์ การโต้ตอบ และการเคลื่อนไหวภายใต้ชุดหลักการและคอมโพเนนต์ที่สอดคล้องกันจะช่วยให้ทีมผลิตภัณฑ์ตระหนักถึงศักยภาพด้านการออกแบบที่ยิ่งใหญ่ที่สุด

logo_components_color_2x_web_96dp.png

Material Components (MDC) ช่วยให้นักพัฒนาแอปใช้งาน Material Design ได้ MDC สร้างขึ้นโดยทีมวิศวกรและนักออกแบบ UX ของ Google โดยมีคอมโพเนนต์ UI ที่สวยงามและใช้งานได้หลายสิบรายการ และพร้อมใช้งานสำหรับ Android, iOS, เว็บ และ Flutter.material.io/develop

ระบบการเคลื่อนไหวของ Material สำหรับ Android คืออะไร

ระบบ Material Motion สำหรับ Android คือชุดรูปแบบการเปลี่ยนภาพภายในไลบรารี MDC-Android ที่ช่วยให้ผู้ใช้เข้าใจและไปยังส่วนต่างๆ ของแอปได้ตามที่อธิบายไว้ในหลักเกณฑ์ Material Design

รูปแบบการเปลี่ยนภาพ Material หลัก 4 รูปแบบมีดังนี้

  • เปลี่ยนรูปแบบคอนเทนเนอร์: การเปลี่ยนระหว่างองค์ประกอบ UI ที่มีคอนเทนเนอร์ สร้างการเชื่อมต่อที่มองเห็นได้ระหว่างองค์ประกอบ UI 2 รายการที่แตกต่างกันโดยการเปลี่ยนองค์ประกอบหนึ่งเป็นอีกองค์ประกอบหนึ่งอย่างราบรื่น
  • แกนร่วม: การเปลี่ยนระหว่างองค์ประกอบ UI ที่มีความสัมพันธ์ในการนำทางหรือเชิงพื้นที่ ใช้การเปลี่ยนรูปแบบร่วมกันบนแกน X, Y หรือ Z เพื่อเสริมความสัมพันธ์ระหว่างองค์ประกอบต่างๆ
  • จางผ่าน: การเปลี่ยนผ่านระหว่างองค์ประกอบ UI ที่ไม่มีความสัมพันธ์ที่มีอิทธิพลต่อกัน ใช้การจางออกและจางเข้าตามลำดับ โดยมีขนาดขององค์ประกอบที่เข้ามา
  • จางลง: ใช้กับองค์ประกอบ UI ที่เข้าหรือออกภายในขอบเขตหน้าจอ

ไลบรารี MDC-Android มีคลาสการเปลี่ยนฉากสำหรับรูปแบบเหล่านี้ ซึ่งสร้างขึ้นจากทั้งไลบรารีการเปลี่ยนฉาก AndroidX (androidx.transition) และเฟรมเวิร์กการเปลี่ยนฉากของ Android (android.transition)

AndroidX

  • พร้อมให้บริการในแพ็กเกจ com.google.android.material.transition
  • รองรับ API ระดับ 14 ขึ้นไป
  • รองรับ Fragment และ View แต่ไม่รองรับ Activity หรือ Window
  • มีการแก้ไขข้อบกพร่องที่ย้อนกลับและลักษณะการทำงานที่สอดคล้องกันในระดับ API ต่างๆ

Framework

  • พร้อมให้บริการในแพ็กเกจ com.google.android.material.transition.platform
  • รองรับ API ระดับ 21 ขึ้นไป
  • รองรับ Fragment, View, Activity และ Window
  • การแก้ไขข้อบกพร่องที่ไม่ได้ย้อนกลับและอาจมีลักษณะการทำงานที่แตกต่างกันใน API ระดับต่างๆ

ใน Codelab นี้ คุณจะได้ใช้การเปลี่ยนภาพ Material ที่สร้างขึ้นบนไลบรารี AndroidX ซึ่งหมายความว่าคุณจะมุ่งเน้นที่ Fragment และ View เป็นหลัก

สิ่งที่คุณจะสร้าง

Codelab นี้จะแนะนำวิธีสร้างทรานซิชันบางอย่างลงในแอปอีเมล Android ตัวอย่างที่ชื่อ Reply โดยใช้ Kotlin เพื่อแสดงวิธีใช้ทรานซิชันจากไลบรารี MDC-Android เพื่อปรับแต่งรูปลักษณ์และความรู้สึกของแอป

เราจะให้โค้ดเริ่มต้นสำหรับแอป Reply และคุณจะรวมการเปลี่ยนภาพของ Material ต่อไปนี้ไว้ในแอป ซึ่งจะเห็นได้ใน GIF ของ Codelab ที่เสร็จสมบูรณ์ด้านล่าง

  • การเปลี่ยนการเปลี่ยนรูปแบบคอนเทนเนอร์จากรายชื่ออีเมลไปยังหน้ารายละเอียดอีเมล
  • การเปลี่ยนจาก FAB ไปยังหน้าเขียนอีเมลด้วยการเปลี่ยนรูปแบบคอนเทนเนอร์
  • การเปลี่ยนแกน Z ที่ใช้ร่วมกันจากไอคอนค้นหาไปยังหน้ามุมมองการค้นหา
  • การเปลี่ยนภาพจางระหว่างหน้ากล่องจดหมาย
  • การเปลี่ยนการเปลี่ยนคอนเทนเนอร์จากชิปอีเมลเป็นมุมมองการ์ด

โดเมนของ 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. เมื่อวิซาร์ดการตั้งค่าเสร็จสิ้นและหน้าต่าง Welcome to Android Studio ปรากฏขึ้น ให้คลิก Open an existing Android Studio project

e3f200327a67a53.png

  1. ไปที่ไดเรกทอรีที่คุณติดตั้งโค้ดตัวอย่างไว้ แล้วเลือกไดเรกทอรีตัวอย่างเพื่อเปิดโปรเจ็กต์
  2. รอสักครู่เพื่อให้ Android Studio สร้างและซิงค์โปรเจ็กต์ ดังที่แสดงโดยตัวบ่งชี้กิจกรรมที่ด้านล่างของหน้าต่าง Android Studio
  1. ในขั้นตอนนี้ Android Studio อาจแสดงข้อผิดพลาดในการสร้างบางอย่างเนื่องจากคุณไม่มี Android SDK หรือเครื่องมือบิลด์ เช่น เครื่องมือที่แสดงด้านล่าง ทำตามวิธีการใน Android Studio เพื่อติดตั้ง/อัปเดตเครื่องมือเหล่านี้และซิงค์โปรเจ็กต์ หากยังคงพบปัญหาอยู่ ให้ทำตามคำแนะนำในการอัปเดตเครื่องมือด้วย เครื่องมือจัดการ SDK

6e026ae171f5b1eb.png

ยืนยันการขึ้นต่อกันของโปรเจ็กต์

โปรเจ็กต์ต้องมีทรัพยากร Dependency ใน MDC-Android library โค้ดตัวอย่างที่คุณดาวน์โหลดควรมีรายการการขึ้นต่อกันนี้อยู่แล้ว แต่เรามาดูการกำหนดค่าเพื่อให้แน่ใจกัน

ไปที่ไฟล์ build.gradle ของโมดูล app และตรวจสอบว่าบล็อก dependencies มีทรัพยากร Dependency ใน MDC-Android

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

เรียกใช้แอปเริ่มต้น

  1. ตรวจสอบว่าการกำหนดค่าบิลด์ทางด้านซ้ายของตัวเลือกอุปกรณ์เป็น app
  2. กดปุ่มเรียกใช้ / เล่นสีเขียวเพื่อสร้างและเรียกใช้แอป

24218d0a6ae25803.png

  1. ในหน้าต่างเลือกเป้าหมายการติดตั้งใช้งาน หากคุณมีอุปกรณ์ Android แสดงอยู่ในอุปกรณ์ที่พร้อมใช้งานแล้ว ให้ข้ามไปยังขั้นตอนที่ 8 หรือคลิกสร้างอุปกรณ์เสมือนใหม่
  2. ในหน้าจอเลือกฮาร์ดแวร์ ให้เลือกอุปกรณ์โทรศัพท์ เช่น Pixel 3 แล้วคลิกถัดไป
  3. ในหน้าจออิมเมจระบบ ให้เลือกเวอร์ชัน Android ล่าสุด โดยควรเลือกระดับ API สูงสุด หากไม่ได้ติดตั้ง ให้คลิกลิงก์ดาวน์โหลดที่แสดงและดาวน์โหลดให้เสร็จสมบูรณ์
  4. คลิกถัดไป
  5. ในหน้าจอ อุปกรณ์เสมือน Android (AVD) ให้ปล่อยการตั้งค่าไว้ตามเดิม แล้วคลิกเสร็จสิ้น
  6. เลือกอุปกรณ์ Android จากกล่องโต้ตอบเป้าหมายการติดตั้งใช้งาน
  7. คลิกตกลง
  8. Android Studio จะสร้างแอป นำไปใช้งาน และเปิดแอปในอุปกรณ์เป้าหมายโดยอัตโนมัติ

สำเร็จ! โค้ดเริ่มต้นสำหรับหน้าแรกของ Reply ควรทำงานในโปรแกรมจำลอง คุณควรเห็นกล่องจดหมายที่มีรายการอีเมล

cc73eb0d0f779035.png

ไม่บังคับ: ลดความเร็วภาพเคลื่อนไหวของอุปกรณ์

เนื่องจาก Codelab นี้เกี่ยวข้องกับการเปลี่ยนฉากที่รวดเร็วแต่ราบรื่น การลดความเร็วของภาพเคลื่อนไหวของอุปกรณ์จึงอาจมีประโยชน์ในการสังเกตรายละเอียดบางอย่างของการเปลี่ยนฉากขณะที่คุณกำลังติดตั้งใช้งาน ซึ่งทำได้โดยใช้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: การ์ดการตั้งค่าด่วน

หรือหากต้องการตั้งค่าการ์ดการตั้งค่าด่วน ให้เปิดใช้การตั้งค่าสำหรับนักพัฒนาแอปในอุปกรณ์ก่อน หากยังไม่ได้ดำเนินการ

  1. เปิดแอป "การตั้งค่า" ของอุปกรณ์
  2. เลื่อนลงไปด้านล่าง แล้วคลิก "เกี่ยวกับอุปกรณ์จำลอง"
  3. เลื่อนลงไปด้านล่างสุด แล้วคลิก "หมายเลขบิลด์" อย่างรวดเร็วจนกว่าจะเปิดใช้การตั้งค่าสำหรับนักพัฒนาแอป

จากนั้นให้ทำดังนี้ในแอป "การตั้งค่า" ของอุปกรณ์เพื่อเปิดใช้การ์ดการตั้งค่าด่วน

  1. คลิกไอคอนค้นหาหรือแถบค้นหาที่ด้านบนของหน้าจอ
  2. พิมพ์ "ไทล์" ในช่องค้นหา
  3. คลิกแถว "การ์ดการตั้งค่าด่วนสำหรับนักพัฒนาแอป"
  4. คลิกสวิตช์ "อัตราการเคลื่อนไหวของหน้าต่าง"

สุดท้ายนี้ ตลอดทั้ง Codelab ให้ดึงหน้าต่างแจ้งเตือนของระบบลงมาจากด้านบนของหน้าจอ แล้วใช้ไอคอน c7e3f98200023f6a.png เพื่อสลับระหว่างภาพเคลื่อนไหวแบบช้าและแบบปกติ

3. ทำความคุ้นเคยกับโค้ดแอปตัวอย่าง

มาดูโค้ดกัน เราได้จัดเตรียมแอปที่ใช้ไลบรารีคอมโพเนนต์การนำทาง Jetpack เพื่อไปยังมาระหว่าง Fragment ต่างๆ ภายใน Activity เดียว ซึ่งก็คือ 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>

โปรดสังเกตว่ามี Fragment ทั้งหมดที่กล่าวถึงข้างต้น โดยตั้งค่า Fragment การเปิดตัวเริ่มต้นเป็น HomeFragment ผ่าน app:startDestination="@id/homeFragment" คำจำกัดความ XML ของกราฟปลายทางของ Fragment นี้ รวมถึงการดำเนินการ จะแจ้งให้โค้ดการนำทาง 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 นี้จะแสดงเต็มหน้าจอและจัดการการเปลี่ยนแปลงการนำทางของ Fragment แบบเต็มหน้าจอทั้งหมดในแอป BottomAppBar และ FloatingActionButton ที่ยึดไว้ ซึ่งอยู่ใน activity_main.xml ด้วย จะวางอยู่เหนือ Fragment ปัจจุบันที่แสดงโดย NavHostFragment ดังนั้นจะแสดงหรือซ่อนขึ้นอยู่กับปลายทางของ Fragment ตามโค้ดแอปตัวอย่างที่ให้ไว้

นอกจากนี้ 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 ของ Reply และ 4 Fragment หลักเพื่อตั้งค่าการเปลี่ยน Material ที่ทำงานร่วมกับวิธีการนำทางต่างๆ ทั่วทั้งแอป

เมื่อคุ้นเคยกับโค้ดเริ่มต้นแล้ว มาใช้การเปลี่ยนฉากแรกกัน

4. เพิ่มการเปลี่ยน Container Transform จากรายชื่ออีเมลไปยังหน้ารายละเอียดอีเมล

โดยเริ่มจากการเพิ่มทรานซิชันเมื่อคลิกอีเมล สำหรับการเปลี่ยนแปลงการนำทางนี้ รูปแบบการเปลี่ยนรูปแบบคอนเทนเนอร์เหมาะอย่างยิ่ง เนื่องจากออกแบบมาสำหรับการเปลี่ยนระหว่างองค์ประกอบ UI ที่มีคอนเทนเนอร์ รูปแบบนี้สร้างการเชื่อมต่อที่มองเห็นได้ระหว่างองค์ประกอบ UI 2 รายการ

ก่อนเพิ่มโค้ดใดๆ ให้ลองเรียกใช้แอป Reply แล้วคลิกอีเมล โดยควรใช้การตัดต่อแบบจัมป์คัตอย่างง่าย ซึ่งหมายความว่าหน้าจอจะถูกแทนที่โดยไม่มีการเปลี่ยนฉาก

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

ทุกอย่างเริ่มดูดีขึ้นแล้ว เมื่อคลิกอีเมลในรายชื่ออีเมล การเปลี่ยนคอนเทนเนอร์ควรขยายรายการในรายการเป็นหน้ารายละเอียดแบบเต็มหน้าจอ แต่โปรดสังเกตว่าการกดกลับจะไม่ยุบอีเมลกลับไปในรายการ นอกจากนี้ รายชื่ออีเมลจะหายไปทันทีเมื่อเริ่มการเปลี่ยนผ่าน โดยจะแสดงพื้นหลังหน้าต่างสีเทา ดังนั้นเราจึงยังไม่หยุดเพียงเท่านี้

หากต้องการแก้ไขการเปลี่ยนฉากกลับ ให้เพิ่ม 2 บรรทัดต่อไปนี้ลงในเมธอด onViewCreated ใน HomeFragment.kt

HomeFragment.kt

postponeEnterTransition()
view.doOnPreDraw { startPostponedEnterTransition() }

ลองเรียกใช้แอปอีกครั้ง การกดกลับหลังจากเปิดอีเมลจะยุบอีเมลกลับไปไว้ในรายการ เยี่ยมไปเลย มาปรับปรุงภาพเคลื่อนไหวกันต่อไป

ปัญหาที่รายชื่ออีเมลหายไปเกิดขึ้นเนื่องจากเมื่อไปยัง Fragment ใหม่โดยใช้ Navigation Component ระบบจะนำ 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 ไปยังหน้าเขียนอีเมล

มาต่อกันที่ Container Transform และเพิ่มการเปลี่ยนจากปุ่มการทำงานแบบลอยไปยัง 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 นำออกและแทนที่

คัดลอกข้อมูลโค้ดด้านล่างลงในเมธอด 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 ที่ใช้ร่วมกันเพื่อเสริมความสัมพันธ์เชิงพื้นที่ระหว่าง 2 หน้าจอและระบุการเลื่อนขึ้น 1 ระดับในลำดับชั้นของแอป

ก่อนเพิ่มโค้ดอื่นๆ ให้ลองเรียกใช้แอปแล้วแตะไอคอนค้นหาที่มุมขวาล่างของหน้าจอ ซึ่งควรจะแสดงหน้าจอมุมมองการค้นหาโดยไม่มีการเปลี่ยน

499e1a677b4216bb.gif

หากต้องการเริ่มต้น ให้ค้นหาวิธี navigateToSearch ใน MainActivity แล้วเพิ่มข้อมูลโค้ดต่อไปนี้ก่อนการเรียกใช้เมธอด NavController navigate เพื่อตั้งค่าการเปลี่ยนฉากออกและกลับเข้าของ Fragment ปัจจุบันใน MaterialSharedAxis Z-Axis

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 ในเชิงลึก เพื่อสร้างเอฟเฟกต์ที่ราบรื่นระหว่าง 2 หน้าจอ

e5c0b0a130e807db.gif

7. เพิ่มการเปลี่ยนภาพแบบจางผ่านระหว่างหน้ากล่องจดหมาย

ในขั้นตอนนี้ เราจะเพิ่มทรานซิชันระหว่างกล่องจดหมายต่างๆ เนื่องจากเราไม่ต้องการเน้นความสัมพันธ์เชิงพื้นที่หรือลำดับชั้น เราจึงจะใช้การจางผ่านเพื่อ "สลับ" ระหว่างรายการอีเมลอย่างง่ายๆ

ก่อนที่จะเพิ่มโค้ดเพิ่มเติม ให้ลองเรียกใช้แอป แตะโลโก้ตอบกลับในแถบแอปด้านล่าง และสลับกล่องจดหมาย รายการอีเมลควรเปลี่ยนโดยไม่มีการเปลี่ยนผ่าน

2c874c0a4588e8fb.gif

หากต้องการเริ่มต้น ให้ค้นหาวิธี navigateToHome ใน MainActivity แล้วเพิ่มข้อมูลโค้ดต่อไปนี้ก่อนการเรียกใช้เมธอด NavController navigate เพื่อตั้งค่าการเปลี่ยน MaterialFadeThrough ออกของ Fragment ปัจจุบัน

MainActivity.kt

currentNavigationFragment?.apply {
   exitTransition = MaterialFadeThrough().apply {
       duration = resources.getInteger(R.integer.reply_motion_duration_large).toLong()
   }
}

จากนั้นเปิด HomeFragment ใน onCreate ให้ตั้งค่า enterTransition ของ Fragment เป็นอินสแตนซ์ใหม่ของ MaterialFadeThrough

HomeFragment.kt

enterTransition = MaterialFadeThrough().apply {
   duration = resources.getInteger(R.integer.reply_motion_duration_large).toLong()
}

เรียกใช้แอปอีกครั้ง เมื่อคุณเปิดลิ้นชักการนำทางด้านล่างและเปลี่ยนกล่องจดหมาย รายการอีเมลปัจจุบันควรจางลงและขยายออก ขณะที่รายการใหม่จางลงและขยายเข้า เยี่ยมไปเลย

f61dfd58ea7bd3fd.gif

8. เพิ่มการเปลี่ยนฉากการเปลี่ยนคอนเทนเนอร์จากชิปอีเมลไปยังมุมมองการ์ด

ในขั้นตอนนี้ คุณจะเพิ่มทรานซิชันที่จะเปลี่ยนชิปเป็นการ์ดป๊อปอัป ที่นี่ใช้การเปลี่ยนรูปแบบคอนเทนเนอร์เพื่อช่วยแจ้งให้ผู้ใช้ทราบว่าการดำเนินการในป๊อปอัปจะส่งผลต่อชิปที่ป๊อปอัปมาจาก

ก่อนเพิ่มโค้ด ให้เรียกใช้แอป Reply คลิกอีเมล คลิก FAB "ตอบกลับ" แล้วลองคลิกชิปรายชื่อติดต่อของผู้รับ ชิปควรหายไปทันที และการ์ดที่มีอีเมลสำหรับรายชื่อติดต่อนั้นควรปรากฏขึ้นโดยไม่มีภาพเคลื่อนไหว

6200c682da2382d5.gif

คุณจะทำงานใน ComposeFragment สำหรับขั้นตอนนี้ เลย์เอาต์ ComposeFragment มีชิปผู้รับ (มองเห็นได้โดยค่าเริ่มต้น) และการ์ดผู้รับ (มองไม่เห็นโดยค่าเริ่มต้น) อยู่แล้ว ชิปผู้รับและการ์ดนี้คือ 2 มุมมองที่คุณจะสร้างการเปลี่ยนคอนเทนเนอร์ระหว่างกัน

หากต้องการเริ่มต้น ให้เปิด 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. เสร็จเรียบร้อย

การใช้โค้ด Kotlin ไม่ถึง 100 บรรทัดและมาร์กอัป XML พื้นฐานบางส่วน ไลบรารี MDC-Android ช่วยให้คุณสร้างทรานซิชันที่สวยงามในแอปที่มีอยู่ซึ่งเป็นไปตามหลักเกณฑ์การออกแบบ Material Design รวมถึงมีลักษณะและทำงานอย่างสอดคล้องกันในอุปกรณ์ Android ทุกเครื่อง

454a47ba96017a25.gif

ขั้นตอนถัดไป

ดูข้อมูลเพิ่มเติมเกี่ยวกับระบบการเคลื่อนไหวของ Material ได้ที่ข้อกำหนดและเอกสารประกอบสำหรับนักพัฒนาซอฟต์แวร์ฉบับเต็ม แล้วลองเพิ่มการเปลี่ยนภาพของ Material ลงในแอป

ขอขอบคุณที่ลองใช้การเคลื่อนไหวของ Material เราหวังว่าคุณจะชอบ Codelab นี้

ฉันทำ Codelab นี้เสร็จได้โดยใช้เวลาและความพยายามที่สมเหตุสมผล

เห็นด้วยอย่างยิ่ง เห็นด้วย เป็นกลาง ไม่เห็นด้วย ไม่เห็นด้วยอย่างยิ่ง

ฉันต้องการใช้ระบบการเคลื่อนไหวของ Material ต่อไปในอนาคต

เห็นด้วยอย่างยิ่ง เห็นด้วย เฉยๆ ไม่เห็นด้วย ไม่เห็นด้วยอย่างยิ่ง