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

เฟรมเวิร์ก

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

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

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

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

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

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

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

cc73eb0d0f779035.png

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

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

สังเกตว่ามีการใส่ข้อมูลแฟรกเมนต์ทั้งหมดที่กล่าวถึงข้างต้นไว้อย่างไร โดยตั้งค่าแฟรกเมนต์การเปิดตัวเริ่มต้นเป็น 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 นี้จะแสดงเต็มหน้าจอและจัดการการเปลี่ยนแปลงการนำทาง Fragment แบบเต็มหน้าจอทั้งหมดในแอป BottomAppBar และ FloatingActionButton แบบ Anchor ซึ่งอยู่ใน activity_main.xml วางอยู่บนส่วนย่อยปัจจุบันที่แสดงโดย 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 นี้ คุณจะได้เจาะลึกรายละเอียดกิจกรรมหลัก 4 อย่างของการตอบและส่วนย่อยหลัก 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 ที่แชร์เพื่อเสริมความสัมพันธ์เชิงพื้นที่ระหว่าง 2 หน้าจอ และระบุการเลื่อนขึ้น 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 ซึ่งจะกำหนดค่าการเปลี่ยน Enter และ Return ของ 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()
}

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

fragment_search.xml

android:transitionGroup="true"

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

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

454a47ba96017a25.gif

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

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

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

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

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

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

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