1. บทนำ
Material Design คือระบบสำหรับการสร้างผลิตภัณฑ์ดิจิทัลที่โดดเด่นและสวยงาม โดยการรวมสไตล์ การสร้างแบรนด์ การโต้ตอบ และการเคลื่อนไหวเข้าด้วยกันภายใต้ชุดหลักการและองค์ประกอบที่สอดคล้องกัน ทำให้ทีมผลิตภัณฑ์ตระหนักถึงศักยภาพด้านการออกแบบที่ดีที่สุดของตนเอง
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 ที่ใช้ร่วมกันเปลี่ยนจากไอคอนการค้นหาเป็นหน้ามุมมองการค้นหา
- การเปลี่ยนภาพแบบจางลงระหว่างหน้ากล่องจดหมาย
- การเปลี่ยนจากการเปลี่ยนรูปแบบคอนเทนเนอร์จากชิปอีเมลเป็นมุมมองการ์ด
สิ่งที่คุณต้องมี
- ความรู้พื้นฐานเกี่ยวกับการพัฒนาแอป 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
- เมื่อวิซาร์ดการตั้งค่าเสร็จสิ้นและหน้าต่างยินดีต้อนรับสู่ Android Studio ปรากฏขึ้น ให้คลิกเปิดโปรเจ็กต์ Android Studio ที่มีอยู่
- ไปที่ไดเรกทอรีที่คุณติดตั้งโค้ดตัวอย่างไว้ แล้วเลือกไดเรกทอรีตัวอย่างเพื่อเปิดโปรเจ็กต์
- รอสักครู่เพื่อให้ Android Studio สร้างและซิงค์โปรเจ็กต์ ดังที่แสดงโดยสัญญาณบอกสถานะกิจกรรมที่ด้านล่างของหน้าต่าง Android Studio
- เมื่อถึงขั้นตอนนี้ Android Studio อาจแสดงข้อผิดพลาดบางอย่างเกี่ยวกับการสร้างเนื่องจากคุณไม่มี Android SDK หรือเครื่องมือสร้าง เช่น เครื่องมือที่แสดงด้านล่าง ทำตามวิธีการใน Android Studio เพื่อติดตั้ง/อัปเดตโปรเจ็กต์เหล่านี้ และซิงค์โปรเจ็กต์ หากยังพบปัญหาอยู่ ให้ทำตามคำแนะนำเกี่ยวกับการอัปเดตเครื่องมือด้วย SDK Manager
ยืนยันทรัพยากร Dependency ของโปรเจ็กต์
โปรเจ็กต์ต้องขึ้นต่อกันในไลบรารี MDC-Android โค้ดตัวอย่างที่คุณดาวน์โหลดมาแสดงทรัพยากร Dependency นี้อยู่แล้ว แต่ลองมาดูการกำหนดค่ากัน
ไปที่ไฟล์ build.gradle
ของโมดูล app
และตรวจสอบว่าบล็อก dependencies
มีการใช้ MDC-Android ดังนี้
implementation 'com.google.android.material:material:1.2.0'
เรียกใช้แอปเริ่มต้น
- ตรวจสอบว่าการกำหนดค่าบิลด์ทางด้านซ้ายของตัวเลือกอุปกรณ์คือ
app
- กดปุ่มเรียกใช้ / เล่นสีเขียวเพื่อสร้างและเรียกใช้แอป
- ในหน้าต่างเลือกเป้าหมายการทำให้ใช้งานได้ หากคุณมีอุปกรณ์ Android อยู่ในอุปกรณ์ที่พร้อมใช้งานแล้ว ให้ข้ามไปที่ขั้นตอนที่ 8 หรือคลิกสร้างอุปกรณ์เสมือนใหม่
- ในหน้าจอเลือกฮาร์ดแวร์ ให้เลือกอุปกรณ์โทรศัพท์ เช่น Pixel 3 แล้วคลิกถัดไป
- ในหน้าจออิมเมจระบบ ให้เลือก Android เวอร์ชันล่าสุด หากควรเป็นระดับ API สูงสุด หากยังไม่ได้ติดตั้ง ให้คลิกลิงก์ดาวน์โหลดที่ปรากฏขึ้น แล้วดำเนินการดาวน์โหลดให้เสร็จ
- คลิกถัดไป
- ในหน้าจอ Android Virtual Device (AVD) ให้ปล่อยการตั้งค่าไว้เหมือนเดิม แล้วคลิกเสร็จสิ้น
- เลือกอุปกรณ์ Android จากกล่องโต้ตอบเป้าหมายการติดตั้งใช้งาน
- คลิกตกลง
- Android Studio จะสร้างแอป ติดตั้งใช้งาน และเปิดแอปในอุปกรณ์เป้าหมายโดยอัตโนมัติ
สำเร็จ! โค้ดเริ่มต้นสําหรับหน้าแรกของ Reply ควรทํางานในโปรแกรมจําลอง คุณควรเห็นกล่องจดหมายที่มีรายการอีเมล
ไม่บังคับ: ทำให้ภาพเคลื่อนไหวของอุปกรณ์ช้าลง
เนื่องจากโค้ดแล็บนี้เกี่ยวข้องกับการเปลี่ยนภาพที่รวดเร็วแต่ดูดี จึงอาจมีประโยชน์ในการทำให้ภาพเคลื่อนไหวของอุปกรณ์ช้าลงเพื่อสังเกตรายละเอียดเล็กๆ น้อยๆ ของการเปลี่ยนภาพขณะที่คุณใช้งาน ซึ่งทำได้โดยใช้คำสั่ง 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: การ์ดการตั้งค่าด่วน
หรือหากต้องการตั้งค่าการ์ดการตั้งค่าด่วน ให้เปิดใช้การตั้งค่าสำหรับนักพัฒนาแอปในอุปกรณ์ก่อนหากยังไม่ได้ดำเนินการก่อนหน้านี้ โดยทำดังนี้
- เปิดแอป "การตั้งค่า" ของอุปกรณ์
- เลื่อนลงไปด้านล่างแล้วคลิก "เกี่ยวกับอุปกรณ์จำลอง"
- เลื่อนลงไปด้านล่างแล้วคลิก "หมายเลขบิลด์" อย่างรวดเร็วจนกว่าจะเปิดใช้การตั้งค่าสำหรับนักพัฒนาซอฟต์แวร์
จากนั้น ให้ทำตามขั้นตอนต่อไปนี้ โดยยังอยู่ในแอป "การตั้งค่า" ของอุปกรณ์ เพื่อเปิดใช้การ์ดการตั้งค่าด่วน
- คลิกไอคอนค้นหาหรือแถบค้นหาที่ด้านบนของหน้าจอ
- พิมพ์ "tiles" ในช่องค้นหา
- คลิกแถว "หน้าต่างการตั้งค่าด่วนสำหรับนักพัฒนาซอฟต์แวร์"
- คลิกสวิตช์ "สัดส่วนของภาพเคลื่อนไหวของหน้าต่าง"
สุดท้าย ใน Codelab ให้ดึงหน้าต่างแจ้งเตือนของระบบลงจากด้านบนของหน้าจอ และใช้ไอคอน เพื่อสลับระหว่างภาพเคลื่อนไหวที่ช้าและความเร็วปกติ
3. ทำความคุ้นเคยกับโค้ดตัวอย่างของแอป
มาดูโค้ดกัน เรามีแอปที่ใช้ไลบรารีคอมโพเนนต์การนำทางของ Jetpack เพื่อไปยังส่วนต่างๆ ระหว่าง Fragment ที่ต่างกัน 2-3 แอป โดยทั้งหมดจะอยู่ใน 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>
สังเกตว่ามีการใส่ข้อมูลแฟรกเมนต์ทั้งหมดที่กล่าวถึงข้างต้นไว้อย่างไร โดยตั้งค่าแฟรกเมนต์การเปิดตัวเริ่มต้นเป็น 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 รายการ
ก่อนเพิ่มโค้ดใดๆ ให้ลองเรียกใช้แอปตอบกลับและคลิกอีเมล เครื่องมือดังกล่าวควรตัดภาพอย่างรวดเร็ว ซึ่งหมายความว่าไม่มีการเปลี่ยนหน้าจอใดๆ ทั้งสิ้น:
เริ่มต้นด้วยการเพิ่มแอตทริบิวต์ 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() }
ลองเรียกใช้แอปอีกครั้ง การกดย้อนกลับหลังจากเปิดอีเมลจะยุบอีเมลกลับไปยังรายการ ดีมาก เรามาปรับปรุงภาพเคลื่อนไหวกันต่อ
ปัญหาของรายชื่ออีเมลหายไปเนื่องจากเมื่อไปยังส่วนย่อยใหม่โดยใช้คอมโพเนนต์การนำทาง ส่วนย่อยปัจจุบันจะถูกนำออกทันทีและแทนที่ด้วยส่วนย่อยใหม่ขาเข้า หากต้องการให้แสดงรายการอีเมลต่อไปแม้จะมีการแทนที่แล้ว ให้เพิ่มการเปลี่ยนการออกไปยัง 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"
ในขั้นตอนนี้ คุณควรเปลี่ยนรูปแบบคอนเทนเนอร์ที่ทำงานได้อย่างสมบูรณ์ การคลิกที่อีเมลจะเป็นการขยายรายการในหน้าจอรายละเอียดในขณะที่ย่อรายการอีเมลลง การกด "กลับ" จะยุบหน้าจอรายละเอียดอีเมลกลับไปเป็นรายการย่อยในขณะที่ปรับขนาดรายการอีเมลให้ใหญ่ขึ้น
5. เพิ่มการเปลี่ยนรูปแบบคอนเทนเนอร์จาก FAB ไปยังหน้าเขียนอีเมล
มาต่อด้วยการเปลี่ยนรูปแบบคอนเทนเนอร์และเพิ่มทรานซิชันจากปุ่มการทำงานแบบลอยไปยัง ComposeFragment
ซึ่งจะขยาย FAB เป็นอีเมลใหม่เพื่อให้ผู้ใช้เขียน ขั้นแรก ให้เรียกใช้แอปอีกครั้ง และคลิก FAB เพื่อดูว่าไม่มีการเปลี่ยนเมื่อเปิดหน้าจอเขียนอีเมล
แม้ว่าเราจะใช้คลาสทรานซิชันเดียวกัน แต่วิธีที่เรากําหนดค่าอินสแตนซ์นี้จะแตกต่างออกไปเนื่องจาก 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 ไปเป็นหน้าจอการเขียนที่มีลักษณะดังต่อไปนี้:
6. เพิ่มการเปลี่ยนแกน Z ที่แชร์จากไอคอนค้นหาลงในหน้ามุมมองการค้นหา
ในขั้นตอนนี้ เราจะเพิ่มการเปลี่ยนจากไอคอนค้นหาไปเป็นมุมมองการค้นหาแบบเต็มหน้าจอ เนื่องจากไม่มีคอนเทนเนอร์ถาวรที่เกี่ยวข้องกับการเปลี่ยนแปลงการนําทางนี้ เราจึงใช้การเปลี่ยนรูปแบบแกน Z ที่แชร์เพื่อเสริมความสัมพันธ์เชิงพื้นที่ระหว่าง 2 หน้าจอ และระบุการเลื่อนขึ้น 1 ระดับในลําดับชั้นของแอปได้
ก่อนเพิ่มโค้ดอื่นๆ ให้ลองเรียกใช้แอปแล้วแตะไอคอนค้นหาที่มุมขวาล่างของหน้าจอ ซึ่งจะเป็นการเปิดหน้าจอมุมมองการค้นหาโดยไม่มีการเปลี่ยนแปลง
ในการเริ่มต้น ให้ค้นหาเมธอด 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 หน้าจอ
7. เพิ่มการเปลี่ยนแบบเฟดผ่านระหว่างหน้ากล่องจดหมาย
ในขั้นตอนนี้ เราจะเพิ่มการเปลี่ยนระหว่างกล่องจดหมายต่างๆ เนื่องจากเราไม่ต้องการเน้นความสัมพันธ์เชิงพื้นที่หรือลำดับชั้น เราจึงใช้การค่อยๆ ผ่านเพื่อ "สลับ" รายการอีเมลต่างๆ อย่างง่ายๆ
ก่อนใส่รหัสเพิ่มเติม ให้ลองเรียกใช้แอป แตะโลโก้ "ตอบกลับ" ในแถบแอปด้านล่าง แล้วเปลี่ยนกล่องจดหมาย รายการอีเมลควรเปลี่ยนแปลงโดยไม่มีการเปลี่ยนผ่าน
ในการเริ่มต้น ให้ค้นหาเมธอด 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()
}
เรียกใช้แอปอีกครั้ง เมื่อเปิดลิ้นชักการนำทางด้านล่างและเปลี่ยนกล่องจดหมาย รายการอีเมลปัจจุบันจะค่อยๆ ปรากฏขึ้นและค่อยๆ ปรากฏขึ้น ในขณะที่รายการใหม่จะค่อยๆ เลือนหายไปและปรับขนาดเข้ามา ดีมาก
8. เพิ่มการเปลี่ยนคอนเทนเนอร์จากชิปอีเมลไปยังมุมมองการ์ด
ในขั้นตอนนี้ คุณจะต้องเพิ่มทรานซิชันที่เปลี่ยนชิปเป็นการ์ดป๊อปอัป ระบบใช้การเปลี่ยนรูปแบบคอนเทนเนอร์ที่นี่เพื่อช่วยแจ้งให้ผู้ใช้ทราบว่าการดำเนินการในป๊อปอัปจะส่งผลต่อชิปที่เป็นต้นทางของป๊อปอัป
ก่อนเพิ่มรหัสใดๆ ให้เปิดแอปตอบกลับ คลิกอีเมล คลิก 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. เสร็จเรียบร้อย
ไลบรารี MDC-Android ช่วยให้คุณสามารถสร้างทรานซิชันที่สวยงามในแอปที่มีอยู่ซึ่งเป็นไปตามหลักเกณฑ์ของ Material Design โดยใช้โค้ด Kotlin ไม่ถึง 100 บรรทัดและมาร์กอัป XML พื้นฐานเพียงไม่กี่รายการ ทั้งยังช่วยให้แอปมีรูปลักษณ์และลักษณะการทำงานที่สอดคล้องกันในทุกอุปกรณ์ Android
ขั้นตอนถัดไป
สำหรับข้อมูลเพิ่มเติมเกี่ยวกับระบบการเคลื่อนไหวแบบ Material ให้อ่านข้อกำหนดและเอกสารสำหรับนักพัฒนาซอฟต์แวร์ฉบับเต็ม แล้วลองเพิ่มการเปลี่ยนของ Material ลงในแอปของคุณ
ขอขอบคุณที่ลองใช้ Material Motion เราหวังว่าคุณจะสนุกกับ Codelab นี้