1. บทนำ
Material Design คือระบบสำหรับการสร้างผลิตภัณฑ์ดิจิทัลที่โดดเด่นและสวยงาม การรวมสไตล์ การสร้างแบรนด์ การโต้ตอบ และการเคลื่อนไหวภายใต้ชุดหลักการและคอมโพเนนต์ที่สอดคล้องกันจะช่วยให้ทีมผลิตภัณฑ์ตระหนักถึงศักยภาพด้านการออกแบบที่ยิ่งใหญ่ที่สุด
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 ที่ใช้ร่วมกันจากไอคอนค้นหาไปยังหน้ามุมมองการค้นหา
- การเปลี่ยนภาพจางระหว่างหน้ากล่องจดหมาย
- การเปลี่ยนการเปลี่ยนคอนเทนเนอร์จากชิปอีเมลเป็นมุมมองการ์ด

สิ่งที่คุณต้องมี
- ความรู้พื้นฐานเกี่ยวกับการพัฒนาแอป Android และ Kotlin
- Android Studio (ดาวน์โหลดที่นี่หากยังไม่มี)
- โปรแกรมจำลองหรืออุปกรณ์ Android (พร้อมใช้งานผ่าน Android Studio)
- โค้ดตัวอย่าง (ดูขั้นตอนถัดไป)
คุณจะให้คะแนนระดับประสบการณ์ในการสร้างแอป Android เท่าใด
2. ตั้งค่าสภาพแวดล้อมในการพัฒนาซอฟต์แวร์
เริ่มต้นใช้งาน Android Studio
เมื่อเปิด Android Studio ควรแสดงหน้าต่างชื่อ "ยินดีต้อนรับสู่ Android Studio" อย่างไรก็ตาม หากเปิดใช้ Android Studio เป็นครั้งแรก ให้ทำตามขั้นตอนวิซาร์ดการตั้งค่า Android Studio โดยใช้ค่าเริ่มต้น ขั้นตอนนี้อาจใช้เวลาหลายนาทีในการดาวน์โหลดและติดตั้งไฟล์ที่จำเป็น คุณจึงปล่อยให้ขั้นตอนนี้ทำงานในเบื้องหลังขณะทำส่วนถัดไปได้
ตัวเลือกที่ 1: โคลนแอป Codelab เริ่มต้นจาก GitHub
หากต้องการโคลน codelab นี้จาก GitHub ให้เรียกใช้คำสั่งต่อไปนี้
git clone https://github.com/material-components/material-components-android-motion-codelab.git cd material-components-android-motion-codelab
ตัวเลือกที่ 2: ดาวน์โหลด ไฟล์ ZIP ของแอป Codelab สำหรับผู้เริ่มต้น
แอปเริ่มต้นจะอยู่ในไดเรกทอรี material-components-android-motion-codelab-develop
โหลดโค้ดเริ่มต้นใน Android Studio
- เมื่อวิซาร์ดการตั้งค่าเสร็จสิ้นและหน้าต่าง Welcome to Android Studio ปรากฏขึ้น ให้คลิก Open an existing Android Studio project

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

ยืนยันการขึ้นต่อกันของโปรเจ็กต์
โปรเจ็กต์ต้องมีทรัพยากร Dependency ใน MDC-Android library โค้ดตัวอย่างที่คุณดาวน์โหลดควรมีรายการการขึ้นต่อกันนี้อยู่แล้ว แต่เรามาดูการกำหนดค่าเพื่อให้แน่ใจกัน
ไปที่ไฟล์ build.gradle ของโมดูล app และตรวจสอบว่าบล็อก dependencies มีทรัพยากร Dependency ใน MDC-Android
implementation 'com.google.android.material:material:1.2.0'
เรียกใช้แอปเริ่มต้น
- ตรวจสอบว่าการกำหนดค่าบิลด์ทางด้านซ้ายของตัวเลือกอุปกรณ์เป็น
app - กดปุ่มเรียกใช้ / เล่นสีเขียวเพื่อสร้างและเรียกใช้แอป

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

ไม่บังคับ: ลดความเร็วภาพเคลื่อนไหวของอุปกรณ์
เนื่องจาก 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: การ์ดการตั้งค่าด่วน
หรือหากต้องการตั้งค่าการ์ดการตั้งค่าด่วน ให้เปิดใช้การตั้งค่าสำหรับนักพัฒนาแอปในอุปกรณ์ก่อน หากยังไม่ได้ดำเนินการ
- เปิดแอป "การตั้งค่า" ของอุปกรณ์
- เลื่อนลงไปด้านล่าง แล้วคลิก "เกี่ยวกับอุปกรณ์จำลอง"
- เลื่อนลงไปด้านล่างสุด แล้วคลิก "หมายเลขบิลด์" อย่างรวดเร็วจนกว่าจะเปิดใช้การตั้งค่าสำหรับนักพัฒนาแอป
จากนั้นให้ทำดังนี้ในแอป "การตั้งค่า" ของอุปกรณ์เพื่อเปิดใช้การ์ดการตั้งค่าด่วน
- คลิกไอคอนค้นหาหรือแถบค้นหาที่ด้านบนของหน้าจอ
- พิมพ์ "ไทล์" ในช่องค้นหา
- คลิกแถว "การ์ดการตั้งค่าด่วนสำหรับนักพัฒนาแอป"
- คลิกสวิตช์ "อัตราการเคลื่อนไหวของหน้าต่าง"
สุดท้ายนี้ ตลอดทั้ง Codelab ให้ดึงหน้าต่างแจ้งเตือนของระบบลงมาจากด้านบนของหน้าจอ แล้วใช้ไอคอน
เพื่อสลับระหว่างภาพเคลื่อนไหวแบบช้าและแบบปกติ
3. ทำความคุ้นเคยกับโค้ดแอปตัวอย่าง
มาดูโค้ดกัน เราได้จัดเตรียมแอปที่ใช้ไลบรารีคอมโพเนนต์การนำทาง Jetpack เพื่อไปยังมาระหว่าง Fragment ต่างๆ ภายใน Activity เดียว ซึ่งก็คือ MainActivity
- HomeFragment: แสดงรายการอีเมล
- EmailFragment: แสดงอีเมลฉบับเต็มฉบับเดียว
- ComposeFragment: อนุญาตให้เขียนอีเมลใหม่
- SearchFragment: แสดงมุมมองการค้นหา
navigation_graph.xml
ก่อนอื่น ให้เปิด navigation_graph.xml ในไดเรกทอรี app -> src -> main -> res -> navigation เพื่อทำความเข้าใจวิธีกำหนดค่ากราฟการนำทางของแอป
<navigation xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="@+id/navigation_graph"
app:startDestination="@id/homeFragment">
<fragment
android:id="@+id/homeFragment"
android:name="com.materialstudies.reply.ui.home.HomeFragment"
android:label="HomeFragment">
<argument...>
<action
android:id="@+id/action_homeFragment_to_emailFragment"
app:destination="@id/emailFragment" />
</fragment>
<fragment
android:id="@+id/emailFragment"
android:name="com.materialstudies.reply.ui.email.EmailFragment"
android:label="EmailFragment">
<argument...>
</fragment>
<fragment
android:id="@+id/composeFragment"
android:name="com.materialstudies.reply.ui.compose.ComposeFragment"
android:label="ComposeFragment">
<argument...>
</fragment>
<fragment
android:id="@+id/searchFragment"
android:name="com.materialstudies.reply.ui.search.SearchFragment"
android:label="SearchFragment" />
<action
android:id="@+id/action_global_homeFragment"
app:destination="@+id/homeFragment"
app:launchSingleTop="true"
app:popUpTo="@+id/navigation_graph"
app:popUpToInclusive="true"/>
<action
android:id="@+id/action_global_composeFragment"
app:destination="@+id/composeFragment" />
<action
android:id="@+id/action_global_searchFragment"
app:destination="@+id/searchFragment" />
</navigation>
โปรดสังเกตว่ามี 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 แล้วคลิกอีเมล โดยควรใช้การตัดต่อแบบจัมป์คัตอย่างง่าย ซึ่งหมายความว่าหน้าจอจะถูกแทนที่โดยไม่มีการเปลี่ยนฉาก

เริ่มต้นด้วยการเพิ่มแอตทริบิวต์ 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))
}
ตอนนี้ลองเรียกใช้แอปอีกครั้ง

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

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

แม้ว่าเราจะใช้คลาสการเปลี่ยนเดียวกัน แต่การกำหนดค่าอินสแตนซ์นี้จะแตกต่างกันเนื่องจาก FAB อยู่ใน MainActivity และ ComposeFragment อยู่ในคอนเทนเนอร์โฮสต์การนำทาง MainActivity
ใน ComposeFragment.kt ให้เพิ่มข้อมูลโค้ดต่อไปนี้ลงในเมธอด onViewCreated โดยตรวจสอบว่าได้นำเข้า androidx.transition เวอร์ชันของ Slide แล้ว
ComposeFragment.kt
enterTransition = MaterialContainerTransform().apply {
startView = requireActivity().findViewById(R.id.fab)
endView = emailCardView
duration = resources.getInteger(R.integer.reply_motion_duration_large).toLong()
scrimColor = Color.TRANSPARENT
containerColor = requireContext().themeColor(R.attr.colorSurface)
startContainerColor = requireContext().themeColor(R.attr.colorSecondary)
endContainerColor = requireContext().themeColor(R.attr.colorSurface)
}
returnTransition = Slide().apply {
duration = resources.getInteger(R.integer.reply_motion_duration_medium).toLong()
addTarget(R.id.email_card_view)
}
นอกจากพารามิเตอร์ที่ใช้ในการกำหนดค่าการเปลี่ยนรูปแบบคอนเทนเนอร์ก่อนหน้าแล้ว เรายังตั้งค่า startView และ endView ด้วยตนเองที่นี่ด้วย คุณสามารถระบุแอตทริบิวต์เหล่านี้ด้วยตนเองได้เมื่อจำเป็น แทนที่จะใช้แอตทริบิวต์ transitionName เพื่อให้ระบบการเปลี่ยนภาพของ Android ทราบว่าควรเปลี่ยนมุมมองใด
ตอนนี้ ให้เรียกใช้แอปอีกครั้ง คุณควรเห็น FAB เปลี่ยนเป็นหน้าจอเขียน (ดู GIF ที่ท้ายขั้นตอนนี้)
เช่นเดียวกับขั้นตอนก่อนหน้า คุณต้องเพิ่มทรานซิชันให้กับ HomeFragment เพื่อไม่ให้หายไปหลังจากที่ ComposeFragment นำออกและแทนที่
คัดลอกข้อมูลโค้ดด้านล่างลงในเมธอด navigateToCompose ใน MainActivity ก่อนการเรียกใช้ NavController navigate
MainActivity.kt
currentNavigationFragment?.apply {
exitTransition = MaterialElevationScale(false).apply {
duration = resources.getInteger(R.integer.reply_motion_duration_large).toLong()
}
reenterTransition = MaterialElevationScale(true).apply {
duration = resources.getInteger(R.integer.reply_motion_duration_large).toLong()
}
}
เพียงเท่านี้ก็เสร็จสิ้นขั้นตอนนี้แล้ว คุณควรมีการเปลี่ยนจาก FAB ไปยังหน้าจอเขียนที่มีลักษณะดังนี้

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

หากต้องการเริ่มต้น ให้ค้นหาวิธี 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 หน้าจอ

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

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

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

คุณจะทำงานใน 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)
เรียกใช้แอปอีกครั้ง การคลิกชิปควรขยายชิปเป็นการ์ด ส่วนการคลิกการ์ดจะยุบการ์ดกลับเป็นชิป เยี่ยมไปเลย

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

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