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

1. บทนำ

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

logo_components_color_2x_web_96dp.png

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

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

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

รูปแบบการเปลี่ยน 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 และมุมมอง แต่ไม่สนับสนุนกิจกรรมหรือ Windows
  • มีการแก้ไขข้อบกพร่องของ Backport และการทำงานที่สอดคล้องกันในระดับ API

เฟรมเวิร์ก

  • มีในแพ็กเกจ com.google.android.material.transition.platform
  • รองรับ API ระดับ 21 ขึ้นไป
  • รองรับส่วนย่อย การดู กิจกรรม และ Windows
  • การแก้ไขข้อบกพร่องไม่ได้รับการพอร์ตไปยังระดับอื่นและอาจมีลักษณะการทำงานที่ต่างกันในระดับ API

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

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

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

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

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

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

6e026ae171f5b1eb.png

ยืนยันทรัพยากร Dependency ของโปรเจ็กต์

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

ไปยังไฟล์ 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. ในหน้าจออุปกรณ์เสมือน (AVD) ของ Android (AVD) ให้ปล่อยการตั้งค่าไว้เหมือนเดิม แล้วคลิกเสร็จสิ้น
  6. เลือกอุปกรณ์ Android จากกล่องโต้ตอบเป้าหมายการทำให้ใช้งานได้
  7. คลิกตกลง
  8. Android Studio จะสร้างแอป ติดตั้งใช้งาน และเปิดแอปในอุปกรณ์เป้าหมายโดยอัตโนมัติ

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

cc73eb0d0f779035.png

ไม่บังคับ: ทำให้ภาพเคลื่อนไหวของอุปกรณ์ช้าลง

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

วิธีที่ 1: คำสั่งเชลล์ของ ADB

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

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

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

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

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

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

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

ปัญหาของรายชื่ออีเมลหายไปเนื่องจากเมื่อไปยังส่วนย่อยใหม่โดยใช้คอมโพเนนต์การนำทาง ส่วนย่อยปัจจุบันจะถูกนำออกทันทีและแทนที่ด้วยส่วนย่อยที่เข้ามาใหม่ของเรา หากต้องการให้แสดงรายการอีเมลต่อไปแม้จะมีการแทนที่แล้ว ให้เพิ่มการเปลี่ยนการออกไปยัง 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()
}

ถัดไป ให้ทำเครื่องหมาย RecyclerView ใน fragment_home.xml เป็นกลุ่มการเปลี่ยน เพื่อไม่ให้ระบบใช้การเปลี่ยน MaterialElevationScale กับหน้าจอหลักโดยรวม แทนที่จะเป็นแต่ละมุมมองในลำดับชั้น

fragment_home.xml

android:transitionGroup="true"

ในขั้นตอนนี้ คุณควรเปลี่ยนรูปแบบคอนเทนเนอร์ที่ทำงานได้อย่างสมบูรณ์ การคลิกอีเมลจะเป็นการขยายรายการในหน้าจอรายละเอียดในขณะที่ย่อรายการอีเมล การกด "กลับ" จะยุบหน้าจอรายละเอียดอีเมลกลับไปเป็นรายการย่อยในขณะที่ปรับขนาดรายการอีเมลให้ใหญ่ขึ้น

9df2b39d5a150418.gif

5. เพิ่มการเปลี่ยนคอนเทนเนอร์จาก FAB เพื่อเขียนหน้าอีเมล

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

d242c9708abd382c.gif

แม้ว่าเราจะใช้คลาสการเปลี่ยนเดียวกัน แต่วิธีกำหนดค่าอินสแตนซ์นี้จะแตกต่างออกไปเนื่องจาก FAB ของเราอยู่ใน MainActivity และ ComposeFragment จะอยู่ภายในคอนเทนเนอร์ของโฮสต์การนำทาง MainActivity

ใน ComposeFragment.kt ให้เพิ่มข้อมูลโค้ดต่อไปนี้ลงในเมธอด onViewCreated แล้วอย่าลืมนำเข้า Slide เวอร์ชัน androidx.transition แล้ว

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

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

499e1a677b4216bb.gif

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

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 แบบ Enter และ Return นั้น

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

สุดท้ายนี้ โปรดทำเครื่องหมาย LinearLayout ใน fragment_search.xml เป็นกลุ่มการเปลี่ยน เพื่อให้มั่นใจว่าระบบจะใช้การเปลี่ยน MaterialSharedAxis กับหน้าจอการค้นหาโดยรวม แทนการนำไปใช้กับแต่ละมุมมองในลำดับชั้น

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. เพิ่มการเปลี่ยนคอนเทนเนอร์จากชิปอีเมลไปยังมุมมองการ์ด

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

ก่อนเพิ่มโค้ดใดๆ ให้เรียกใช้แอปตอบกลับ คลิกอีเมล แล้วคลิก "ตอบกลับ" 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. เสร็จเรียบร้อย

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

454a47ba96017a25.gif

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

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

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

ฉันทำ Codelab นี้เสร็จได้ โดยใช้เวลาและลงแรงพอสมควร

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

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

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