1. ก่อนเริ่มต้น
Codelab นี้เป็นส่วนหนึ่งของหลักสูตร Advanced Android ใน Kotlin คุณจะได้รับประโยชน์สูงสุดจากหลักสูตรนี้หากทำงานผ่าน Codelab ตามลำดับ แต่ไม่ได้เป็นข้อบังคับ Codelab ทั้งหมดของหลักสูตรแสดงอยู่ในหน้า Landing Page สำหรับ Android ขั้นสูงใน Kotlin
MotionLayout
คือไลบรารีที่ให้คุณเพิ่มการเคลื่อนไหวที่สมบูรณ์ลงในแอป Android ซึ่งอิงกับ ConstraintLayout,
และให้คุณสร้างภาพเคลื่อนไหวอะไรก็ได้ที่คุณสามารถสร้างโดยใช้ ConstraintLayout
คุณสามารถใช้ MotionLayout
เพื่อทำให้สถานที่ ขนาด การมองเห็น อัลฟ่า สี ระดับความสูง การหมุน และแอตทริบิวต์อื่นๆ ของมุมมองต่างๆ เคลื่อนไหวพร้อมกันได้ การใช้ XML แบบประกาศจะช่วยให้คุณสร้างภาพเคลื่อนไหวแบบประสานกันที่มีมุมมองหลายมุมมองซึ่งทำได้ยากในโค้ด
ภาพเคลื่อนไหวเป็นวิธีที่ยอดเยี่ยมในการปรับปรุงประสบการณ์การใช้งานแอป คุณใช้ภาพเคลื่อนไหวเพื่อทำสิ่งต่อไปนี้ได้
- แสดงการเปลี่ยนแปลง - การเคลื่อนไหวระหว่างสถานะจะช่วยให้ผู้ใช้ติดตามการเปลี่ยนแปลงใน UI ได้อย่างเป็นธรรมชาติ
- ดึงดูดความสนใจ - ใช้ภาพเคลื่อนไหวเพื่อดึงดูดความสนใจไปยังองค์ประกอบ UI ที่สำคัญ
- สร้างดีไซน์ที่สวยงาม การเคลื่อนไหวที่มีประสิทธิภาพในการออกแบบจะทำให้แอปดูสวยงาม
ข้อกำหนดเบื้องต้น
Codelab นี้ได้รับการออกแบบมาสำหรับนักพัฒนาซอฟต์แวร์ที่มีประสบการณ์ด้านการพัฒนาซอฟต์แวร์ Android มาบ้างแล้ว ก่อนที่จะพยายามทำ Codelab นี้ให้เสร็จสิ้น คุณควรทำดังนี้
- รู้วิธีสร้างแอปด้วยกิจกรรม เลย์เอาต์พื้นฐาน และเรียกใช้ในอุปกรณ์หรือโปรแกรมจำลองโดยใช้ Android Studio ทำความคุ้นเคยกับ
ConstraintLayout
อ่าน Constraint Layout Codelab เพื่อดูข้อมูลเพิ่มเติมเกี่ยวกับConstraintLayout
สิ่งที่คุณจะต้องทำ
- กำหนดภาพเคลื่อนไหวด้วย
ConstraintSets
และMotionLayout
- เคลื่อนไหวโดยอิงจากเหตุการณ์การลาก
- เปลี่ยนภาพเคลื่อนไหวด้วย
KeyPosition
- เปลี่ยนแอตทริบิวต์ด้วย
KeyAttribute
- เรียกใช้ภาพเคลื่อนไหวด้วยโค้ด
- ทำให้ส่วนหัวที่ยุบได้เป็นภาพเคลื่อนไหวด้วย
MotionLayout
สิ่งที่คุณต้องมี
- Android Studio 4.0 (ตัวแก้ไข
MotionLayout
ใช้งานได้กับ Android Studio เวอร์ชันนี้เท่านั้น)
2. เริ่มต้นใช้งาน
หากต้องการดาวน์โหลดแอปตัวอย่าง ให้ดำเนินการอย่างใดอย่างหนึ่งต่อไปนี้
... หรือโคลนที่เก็บ GitHub จากบรรทัดคำสั่งโดยใช้คำสั่งต่อไปนี้
$ git clone https://github.com/googlecodelabs/motionlayout.git
3. การสร้างภาพเคลื่อนไหวด้วย MotionLayout
ประการแรก คุณจะต้องสร้างภาพเคลื่อนไหวที่ย้ายมุมมองจากด้านบนเริ่มต้นของหน้าจอไปยังด้านล่างสุดเพื่อตอบสนองต่อการคลิกของผู้ใช้
หากต้องการสร้างภาพเคลื่อนไหวจากโค้ดเริ่มต้น คุณจะต้องมีส่วนประกอบหลักๆ ดังต่อไปนี้
MotionLayout,
ซึ่งเป็นคลาสย่อยของConstraintLayout
คุณระบุมุมมองทั้งหมดให้เป็นภาพเคลื่อนไหวภายในแท็กMotionLayout
MotionScene,
ซึ่งเป็นไฟล์ XML ที่อธิบายภาพเคลื่อนไหวสำหรับMotionLayout.
Transition,
ซึ่งเป็นส่วนหนึ่งของMotionScene
ที่ระบุระยะเวลาของภาพเคลื่อนไหว ทริกเกอร์ และวิธีย้ายมุมมองConstraintSet
ที่ระบุทั้งข้อจำกัด start และ end ของการเปลี่ยน
เรามาดูแต่ละปัจจัยตามลำดับกันเลย เริ่มจาก MotionLayout
ขั้นตอนที่ 1: สำรวจโค้ดที่มีอยู่
MotionLayout
เป็นคลาสย่อยของ ConstraintLayout
ดังนั้นจึงรองรับฟีเจอร์เดียวกันทั้งหมดขณะเพิ่มภาพเคลื่อนไหว หากต้องการใช้ MotionLayout
คุณจะต้องเพิ่มข้อมูลพร็อพเพอร์ตี้ MotionLayout
ที่คุณจะใช้ ConstraintLayout.
- ใน
res/layout
ให้เปิดactivity_step1.xml.
ที่นี่คุณมีConstraintLayout
ที่มีดาวImageView
ดวงเดียว ซึ่งมีการทาสีด้านใน
activity_step1.xml
<!-- initial code -->
<androidx.constraintlayout.widget.ConstraintLayout
...
android:layout_width="match_parent"
android:layout_height="match_parent"
>
<ImageView
android:id="@+id/red_star"
...
/>
</androidx.constraintlayout.motion.widget.MotionLayout>
ConstraintLayout
นี้ไม่มีข้อจำกัดใดๆ ดังนั้นถ้าคุณเรียกใช้แอปตอนนี้ คุณจะเห็นการแสดงดาวไม่จำกัด ซึ่งหมายความว่าแอปจะอยู่ในตำแหน่งที่ไม่รู้จัก Android Studio จะแจ้งเตือนเกี่ยวกับการไม่มีข้อจำกัด
ขั้นตอนที่ 2: แปลงเป็นเลย์เอาต์แบบเคลื่อนไหว
หากต้องการให้เคลื่อนไหวโดยใช้ MotionLayout,
คุณต้องแปลง ConstraintLayout
เป็น MotionLayout
เลย์เอาต์จะต้องชี้ไปที่ฉากนั้น เลย์เอาต์จึงจะใช้ฉากเคลื่อนไหวได้
- โดยเปิดแพลตฟอร์มการออกแบบ ใน Android Studio 4.0 คุณจะเปิดพื้นผิวการออกแบบโดยใช้ไอคอนแยกหรือไอคอนการออกแบบที่ด้านขวาบนเมื่อดูไฟล์ XML ของเลย์เอาต์
- เมื่อคุณเปิดพื้นที่การออกแบบแล้ว ให้คลิกขวาที่ตัวอย่างและเลือกแปลงเป็น MotionLayout
การดำเนินการนี้จะแทนที่แท็ก ConstraintLayout
ด้วยแท็ก MotionLayout
และเพิ่ม motion:layoutDescription
ลงในแท็ก MotionLayout
ที่ชี้ไปยัง @xml/activity_step1_scene.
activity_step1**.xml**
<!-- explore motion:layoutDescription="@xml/activity_step1_scene" -->
<androidx.constraintlayout.motion.widget.MotionLayout
...
motion:layoutDescription="@xml/activity_step1_scene">
ฉากเคลื่อนไหวคือไฟล์ XML ไฟล์เดียวที่อธิบายภาพเคลื่อนไหวใน MotionLayout
เมื่อคุณแปลงเป็น MotionLayout
แพลตฟอร์มการออกแบบจะแสดง Motion Editor
องค์ประกอบ UI ใหม่ 3 รายการใน Motion Editor มีดังนี้
- ภาพรวม – นี่คือการเลือกโมดัลที่ให้คุณสามารถเลือกส่วนต่างๆ ของภาพเคลื่อนไหว ในภาพนี้ระบบเลือก
start
ConstraintSet
นอกจากนี้ คุณยังสามารถเลือกการเปลี่ยนแปลงระหว่างstart
และend
ได้โดยคลิกที่ลูกศรที่อยู่ตรงกลาง - ส่วน - ด้านล่างของภาพรวมคือหน้าต่างหัวข้อที่เปลี่ยนแปลงตามรายการภาพรวมที่เลือกไว้ในปัจจุบัน ในภาพนี้ ข้อมูล
start
ConstraintSet
จะแสดงในหน้าต่างการเลือก - แอตทริบิวต์ – แผงแอตทริบิวต์จะแสดงและช่วยให้คุณแก้ไขแอตทริบิวต์ของรายการที่เลือกในปัจจุบันได้จากหน้าต่างภาพรวมหรือหน้าต่างการเลือก ในภาพนี้แสดงแอตทริบิวต์ของ
start
ConstraintSet
ขั้นตอนที่ 3: กำหนดข้อจำกัดเริ่มต้นและสิ้นสุด
ภาพเคลื่อนไหวทั้งหมดสามารถกำหนดจุดเริ่มต้นและจุดสิ้นสุดได้ จุดเริ่มต้นจะอธิบายลักษณะของหน้าจอก่อนที่จะแสดงภาพเคลื่อนไหว และตอนท้ายจะอธิบายลักษณะของหน้าจอหลังจากที่ภาพเคลื่อนไหวจบลงแล้ว MotionLayout
มีหน้าที่หาวิธีภาพเคลื่อนไหวระหว่างสถานะเริ่มต้นกับสถานะสิ้นสุด (เมื่อเวลาผ่านไป)
MotionScene
ใช้แท็ก ConstraintSet
เพื่อกำหนดสถานะเริ่มต้นและสิ้นสุด ConstraintSet
คือชุดข้อจำกัดที่นำไปใช้กับมุมมองได้ ซึ่งรวมถึงข้อจำกัดความกว้าง ความสูง และ ConstraintLayout
รวมถึงแอตทริบิวต์บางรายการด้วย เช่น alpha
โดยไม่ได้มีมุมมองเพียงอย่างเดียว แต่เป็นเพียงข้อจำกัดของมุมมองเท่านั้น
ข้อจำกัดใดๆ ที่ระบุใน ConstraintSet
จะลบล้างข้อจำกัดที่ระบุไว้ในไฟล์เลย์เอาต์ หากคุณกำหนดข้อจำกัดทั้งในเลย์เอาต์และ MotionScene
ระบบจะนำเฉพาะข้อจำกัดใน MotionScene
ไปใช้เท่านั้น
ในขั้นตอนนี้ คุณจะจำกัดการดูดาวเพื่อให้เริ่มที่จุดเริ่มต้นของหน้าจอและสิ้นสุดที่ด้านล่างของหน้าจอ
คุณสามารถทำขั้นตอนนี้ให้เสร็จโดยใช้เครื่องมือแก้ไขภาพเคลื่อนไหวหรือแก้ไขข้อความของ activity_step1_scene.xml
โดยตรง
- เลือก ConstraintSet ของ
start
ในแผงภาพรวม
- ในแผงการเลือก ให้เลือก
red_star
ปัจจุบันแสดงแหล่งที่มาของlayout
ซึ่งหมายความว่าไม่มีการจํากัดในConstraintSet
นี้ ใช้ไอคอนดินสอที่ด้านขวาบนเพื่อสร้างข้อจํากัด
- ยืนยันว่า
red_star
แสดงแหล่งที่มาstart
เมื่อเลือกConstraintSet
ของstart
ในแผงภาพรวม - ในแผงแอตทริบิวต์ ที่มีการเลือก
red_star
ในConstraintSet
ของstart
ให้เพิ่มข้อจำกัดที่ด้านบน แล้วเริ่มด้วยการคลิกปุ่ม + สีน้ำเงิน
- เปิด
xml/activity_step1_scene.xml
เพื่อดูโค้ดที่ Motion Editor สร้างขึ้นสำหรับข้อจำกัดนี้
activity_step1_scene.xml
<!-- Constraints to apply at the start of the animation -->
<ConstraintSet android:id="@+id/start">
<Constraint
android:id="@+id/red_star"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
motion:layout_constraintStart_toStartOf="parent"
motion:layout_constraintTop_toTopOf="parent" />
</ConstraintSet>
ConstraintSet
มี id
เป็น @id/start
และระบุข้อจำกัดทั้งหมดที่จะนำไปใช้กับการแสดงผลทั้งหมดใน MotionLayout
เนื่องจาก MotionLayout
นี้มีข้อมูลพร็อพเพอร์ตี้เดียว จึงต้องการใช้ Constraint
เพียง 1 รายการ
Constraint
ภายใน ConstraintSet
ระบุรหัสของข้อมูลพร็อพเพอร์ตี้ที่มีการจำกัด @id/red_star
ซึ่งกำหนดไว้ใน activity_step1.xml
โปรดทราบว่าแท็ก Constraint
ระบุเฉพาะข้อจำกัดและข้อมูลเลย์เอาต์เท่านั้น แท็ก Constraint
ไม่ทราบว่ากําลังใช้กับ ImageView
อยู่
ข้อจำกัดนี้ระบุความสูง ความกว้าง และข้อจำกัดอื่นอีก 2 รายการที่จำเป็นในการจำกัดมุมมอง red_star
ไปยังจุดเริ่มต้นของระดับบนสุด
- เลือก ConstraintSet ของ
end
ในแผงภาพรวม
- ทำตามขั้นตอนเดิมเพื่อเพิ่ม
Constraint
สำหรับred_star
ในConstraintSet
ของend
- หากต้องการใช้ Motion Editor เพื่อดำเนินการขั้นตอนนี้ ให้เพิ่มข้อจำกัดลงใน
bottom
และend
โดยคลิกปุ่ม + สีน้ำเงิน
- โค้ดใน XML มีลักษณะดังนี้
activitiy_step1_scene.xml
<!-- Constraints to apply at the end of the animation -->
<ConstraintSet android:id="@+id/end">
<Constraint
android:id="@+id/red_star"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
motion:layout_constraintEnd_toEndOf="parent"
motion:layout_constraintBottom_toBottomOf="parent" />
</ConstraintSet>
ConstraintSet
นี้มี Constraint
รายการเดียวใน @id/red_star
เหมือนกับ @id/start
ซึ่งตอนนี้จะจำกัดให้อยู่ที่ด้านล่างของหน้าจอ
คุณไม่จำเป็นต้องตั้งชื่อว่า @id/start
และ @id/end
แต่ใช้ชื่อนี้ได้สะดวก
ขั้นตอนที่ 4: กำหนดการเปลี่ยน
MotionScene
ทุกรายการต้องมีการเปลี่ยนอย่างน้อย 1 ครั้งด้วย การเปลี่ยนภาพจะกำหนดทุกส่วนของภาพเคลื่อนไหว 1 ภาพตั้งแต่ต้นจนจบ
การเปลี่ยนต้องระบุจุดเริ่มต้นและสิ้นสุด ConstraintSet
การเปลี่ยนยังระบุวิธีแก้ไขภาพเคลื่อนไหวในลักษณะอื่นๆ ได้ด้วย เช่น ระยะเวลาที่จะเรียกใช้ภาพเคลื่อนไหว หรือวิธีทำให้ภาพเคลื่อนไหวด้วยการลากมุมมอง
- Motion Editor จะสร้างการเปลี่ยนสำหรับเราโดยค่าเริ่มต้นเมื่อสร้างไฟล์ MotionScene เปิด
activity_step1_scene.xml
เพื่อดูการเปลี่ยนที่สร้างขึ้น
activity_step1_scene.xml
<!-- A transition describes an animation via start and end state -->
<Transition
motion:constraintSetEnd="@+id/end"
motion:constraintSetStart="@id/start"
motion:duration="1000">
<KeyFrameSet>
</KeyFrameSet>
</Transition>
นี่คือทุกสิ่งที่ MotionLayout
ต้องใช้ในการสร้างภาพเคลื่อนไหว โดยดูที่แอตทริบิวต์แต่ละรายการ
- ระบบจะใช้
constraintSetStart
กับมุมมองเมื่อภาพเคลื่อนไหวเริ่มขึ้น - ระบบจะใช้
constraintSetEnd
กับมุมมองที่ตอนท้ายของภาพเคลื่อนไหว duration
ระบุระยะเวลาที่ภาพเคลื่อนไหวควรจะแสดงในหน่วยมิลลิวินาที
จากนั้น MotionLayout
จะกำหนดเส้นทางระหว่างข้อจำกัดเริ่มต้นและสิ้นสุด แล้วสร้างภาพเคลื่อนไหวตามระยะเวลาที่ระบุ
ขั้นตอนที่ 5: แสดงตัวอย่างภาพเคลื่อนไหวในเครื่องมือแก้ไขภาพเคลื่อนไหว
ภาพเคลื่อนไหว: วิดีโอแสดงตัวอย่างการเปลี่ยนใน Motion Editor
- เปิด Motion Editor และเลือกการเปลี่ยนโดยคลิกลูกศรระหว่าง
start
และend
ในแผงภาพรวม
- แผงการเลือกจะแสดงตัวควบคุมการเล่นและแถบกรอเมื่อเลือกการเปลี่ยน คลิกเล่นหรือลากตำแหน่งปัจจุบันเพื่อดูตัวอย่างภาพเคลื่อนไหว
ขั้นตอนที่ 6: เพิ่ม Click Handler
คุณต้องมีวิธีเริ่มภาพเคลื่อนไหว วิธีหนึ่งในการทำเช่นนี้คือการทำให้ MotionLayout
ตอบสนองต่อกิจกรรมการคลิกใน @id/red_star
- เปิดเครื่องมือแก้ไขการเคลื่อนไหวแล้วเลือกการเปลี่ยนโดยคลิกลูกศรที่อยู่ระหว่างจุดเริ่มต้นและจุดสิ้นสุดในแผงภาพรวม
- คลิก สร้างเครื่องจัดการการคลิกหรือเลื่อนในแถบเครื่องมือสำหรับแผงภาพรวม การดำเนินการนี้จะเพิ่มตัวจัดการที่จะเริ่มการเปลี่ยน
- เลือกคลิกเครื่องจัดการจากป๊อปอัป
- เปลี่ยนมุมมองเพื่อคลิกเป็น
red_star
- คลิกเพิ่มที่เครื่องจัดการการคลิกจะแสดงด้วยจุดเล็กๆ บน "การเปลี่ยน" ใน Motion Editor
- เมื่อเลือกการเปลี่ยนในแผงภาพรวม ให้เพิ่มแอตทริบิวต์
clickAction
ของtoggle
ในเครื่องจัดการ OnClick ที่เพิ่งเพิ่มในแผงแอตทริบิวต์
- เปิด
activity_step1_scene.xml
เพื่อดูโค้ดที่ Motion Editor สร้างขึ้น
activity_step1_scene.xml
<!-- A transition describes an animation via start and end state -->
<Transition
motion:constraintSetStart="@+id/start"
motion:constraintSetEnd="@+id/end"
motion:duration="1000">
<!-- MotionLayout will handle clicks on @id/red_star to "toggle" the animation between the start and end -->
<OnClick
motion:targetId="@id/red_star"
motion:clickAction="toggle" />
</Transition>
Transition
จะบอกให้ MotionLayout
เรียกใช้ภาพเคลื่อนไหวเพื่อตอบสนองต่อเหตุการณ์การคลิกโดยใช้แท็ก <OnClick>
โดยดูที่แอตทริบิวต์แต่ละรายการ
targetId
คือจำนวนการดูที่น่าจับตามองสำหรับการคลิกclickAction
จากtoggle
จะสลับระหว่างสถานะเริ่มต้นและสิ้นสุดเมื่อคลิก คุณดูตัวเลือกอื่นๆ ของclickAction
ได้ในเอกสารประกอบ
- รันโค้ดของคุณ คลิกขั้นตอนที่ 1 แล้วคลิกดาวสีแดงและดูภาพเคลื่อนไหว!
ขั้นตอนที่ 5: การทำงานของภาพเคลื่อนไหว
เรียกใช้แอป คุณควรเห็นภาพเคลื่อนไหวทำงานเมื่อคลิกที่ดาว
ไฟล์ฉากเคลื่อนไหวที่เสร็จสมบูรณ์จะกำหนด Transition
1 รายการที่ชี้ไปยังจุดเริ่มต้นและสิ้นสุด ConstraintSet
ที่จุดเริ่มต้นของภาพเคลื่อนไหว (@id/start
) ไอคอนดาวจะจำกัดอยู่ที่จุดเริ่มต้นของหน้าจอ ในช่วงท้ายของภาพเคลื่อนไหว (@id/end
) ไอคอนดาวจะถูกจำกัดอยู่ที่ด้านล่างของหน้าจอ
<?xml version="1.0" encoding="utf-8"?>
<!-- Describe the animation for activity_step1.xml -->
<MotionScene xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:android="http://schemas.android.com/apk/res/android">
<!-- A transition describes an animation via start and end state -->
<Transition
motion:constraintSetStart="@+id/start"
motion:constraintSetEnd="@+id/end"
motion:duration="1000">
<!-- MotionLayout will handle clicks on @id/star to "toggle" the animation between the start and end -->
<OnClick
motion:targetId="@id/red_star"
motion:clickAction="toggle" />
</Transition>
<!-- Constraints to apply at the end of the animation -->
<ConstraintSet android:id="@+id/start">
<Constraint
android:id="@+id/red_star"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
motion:layout_constraintStart_toStartOf="parent"
motion:layout_constraintTop_toTopOf="parent" />
</ConstraintSet>
<!-- Constraints to apply at the end of the animation -->
<ConstraintSet android:id="@+id/end">
<Constraint
android:id="@+id/red_star"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
motion:layout_constraintEnd_toEndOf="parent"
motion:layout_constraintBottom_toBottomOf="parent" />
</ConstraintSet>
</MotionScene>
4. ภาพเคลื่อนไหวตามเหตุการณ์การลาก
ในขั้นตอนนี้ คุณจะสร้างภาพเคลื่อนไหวที่ตอบสนองต่อเหตุการณ์การลากของผู้ใช้ (เมื่อผู้ใช้ปัดหน้าจอ) เพื่อเรียกใช้ภาพเคลื่อนไหว MotionLayout
รองรับการติดตามกิจกรรมการสัมผัสเพื่อย้ายมุมมอง รวมถึงท่าทางสัมผัสการสะบัดตามหลักฟิสิกส์เพื่อให้การเคลื่อนไหวลื่นไหล
ขั้นตอนที่ 1: ตรวจสอบโค้ดเริ่มต้น
- ในการเริ่มต้นใช้งาน ให้เปิดไฟล์เลย์เอาต์
activity_step2.xml
ซึ่งมีMotionLayout
อยู่แล้ว ตรวจสอบโค้ด
activity_step2.xml
<!-- initial code -->
<androidx.constraintlayout.motion.widget.MotionLayout
...
motion:layoutDescription="@xml/step2" >
<ImageView
android:id="@+id/left_star"
...
/>
<ImageView
android:id="@+id/right_star"
...
/>
<ImageView
android:id="@+id/red_star"
...
/>
<TextView
android:id="@+id/credits"
...
motion:layout_constraintTop_toTopOf="parent"
motion:layout_constraintEnd_toEndOf="parent"/>
</androidx.constraintlayout.motion.widget.MotionLayout>
เค้าโครงนี้จะกำหนดมุมมองทั้งหมดสำหรับภาพเคลื่อนไหว ไอคอนดาว 3 ดวงจะไม่ถูกจำกัดในเลย์เอาต์เนื่องจากไอคอนจะเคลื่อนไหวในฉากเคลื่อนไหว
เครดิต TextView
มีข้อจำกัดในการใช้เนื่องจากอยู่ที่เดิมสำหรับภาพเคลื่อนไหวทั้งหมดและไม่แก้ไขแอตทริบิวต์ใดๆ
ขั้นตอนที่ 2: ทำให้ฉากเคลื่อนไหว
ภาพเคลื่อนไหวจะกำหนดโดย ConstraintSet,
เริ่มต้นและสิ้นสุดและ Transition
เช่นเดียวกับภาพเคลื่อนไหวล่าสุด
กำหนดการเริ่มต้น ConstraintSet
- เปิดฉากเคลื่อนไหว
xml/step2.xml
เพื่อกำหนดภาพเคลื่อนไหว - เพิ่มข้อจำกัดสำหรับข้อจำกัดเริ่มต้น
start
ตอนเริ่มต้น ดาวทั้ง 3 ดวงจะอยู่ตรงกลางด้านล่างของหน้าจอ ดาวด้านขวาและซ้ายมีค่าalpha
เป็น0.0
ซึ่งหมายความว่ามีความโปร่งใสและซ่อนไว้ทั้งหมด
step2.xml
<!-- TODO apply starting constraints -->
<!-- Constraints to apply at the start of the animation -->
<ConstraintSet android:id="@+id/start">
<Constraint
android:id="@+id/red_star"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
motion:layout_constraintStart_toStartOf="parent"
motion:layout_constraintEnd_toEndOf="parent"
motion:layout_constraintBottom_toBottomOf="parent" />
<Constraint
android:id="@+id/left_star"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:alpha="0.0"
motion:layout_constraintStart_toStartOf="parent"
motion:layout_constraintEnd_toEndOf="parent"
motion:layout_constraintBottom_toBottomOf="parent" />
<Constraint
android:id="@+id/right_star"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:alpha="0.0"
motion:layout_constraintStart_toStartOf="parent"
motion:layout_constraintEnd_toEndOf="parent"
motion:layout_constraintBottom_toBottomOf="parent" />
</ConstraintSet>
ใน ConstraintSet
นี้ คุณต้องระบุ Constraint
1 รายการสำหรับดาวแต่ละดวง MotionLayout
จะใช้ข้อจำกัดแต่ละข้อเมื่อเริ่มต้นภาพเคลื่อนไหว
มุมมองดาวแต่ละมุมมองจะอยู่ตรงกลางที่ด้านล่างของหน้าจอโดยใช้ข้อจำกัดเริ่มต้น สิ้นสุด และด้านล่าง ทั้ง 2 ดาว @id/left_star
และ @id/right_star
มีค่าอัลฟ่าเพิ่มเติมที่ทำให้มองไม่เห็น ซึ่งจะนำมาใช้เมื่อเริ่มภาพเคลื่อนไหว
ชุดข้อจำกัด start
และ end
กำหนดจุดเริ่มต้นและจุดสิ้นสุดของภาพเคลื่อนไหว ข้อจำกัดในการเริ่มต้น เช่น motion:layout_constraintStart_toStartOf
จะจำกัดจุดเริ่มต้นของมุมมองไปยังจุดเริ่มต้นของอีกมุมมองหนึ่ง สิ่งนี้อาจทำให้สับสนในตอนแรก เนื่องจากชื่อ start
ใช้กับทั้ง และ ทั้งคู่ในบริบทของข้อจำกัด เพื่อช่วยให้แยกแยะความแตกต่างได้ start
ใน layout_constraintStart
จะหมายถึง "start" ของมุมมอง ซึ่งแสดงด้านซ้ายเป็นภาษาจากซ้ายไปขวาและในภาษาจากขวาไปซ้าย ชุดข้อจำกัด start
อ้างถึงจุดเริ่มต้นของภาพเคลื่อนไหว
กำหนด ConstraintSet ส่วนท้าย
- กำหนดจุดยึดสุดท้ายเพื่อใช้เชนเพื่อจัดตำแหน่งดาวทั้ง 3 ดวงให้อยู่ด้านล่าง
@id/credits
นอกจากนี้ยังกำหนดค่าสิ้นสุดของalpha
ของดาวด้านซ้ายและขวาเป็น1.0
ด้วย
step2.xml
<!-- TODO apply ending constraints -->
<!-- Constraints to apply at the end of the animation -->
<ConstraintSet android:id="@+id/end">
<Constraint
android:id="@+id/left_star"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:alpha="1.0"
motion:layout_constraintHorizontal_chainStyle="packed"
motion:layout_constraintStart_toStartOf="parent"
motion:layout_constraintEnd_toStartOf="@id/red_star"
motion:layout_constraintTop_toBottomOf="@id/credits" />
<Constraint
android:id="@+id/red_star"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
motion:layout_constraintStart_toEndOf="@id/left_star"
motion:layout_constraintEnd_toStartOf="@id/right_star"
motion:layout_constraintTop_toBottomOf="@id/credits" />
<Constraint
android:id="@+id/right_star"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:alpha="1.0"
motion:layout_constraintStart_toEndOf="@id/red_star"
motion:layout_constraintEnd_toEndOf="parent"
motion:layout_constraintTop_toBottomOf="@id/credits" />
</ConstraintSet>
ผลลัพธ์สุดท้ายคือมุมมองจะกระจายออกไปจากจุดศูนย์กลางเมื่อมีการเคลื่อนไหว
นอกจากนี้ เนื่องจากตั้งค่าพร็อพเพอร์ตี้ alpha
บน @id/right_start
และ @id/left_star
ใน ConstraintSets
ทั้ง 2 มุมมอง มุมมองทั้ง 2 จะจางลงเมื่อภาพเคลื่อนไหวดำเนินไป
ภาพเคลื่อนไหวตามการปัดของผู้ใช้
MotionLayout
สามารถติดตามเหตุการณ์การลากหรือการปัดของผู้ใช้เพื่อสร้าง "การสะบัด" ตามหลักฟิสิกส์ได้ ภาพเคลื่อนไหว ซึ่งหมายความว่ามุมมองจะดำเนินต่อไปหากผู้ใช้สะบัดหน้าจอ และจะช้าลงเช่นเดียวกับวัตถุจริงเมื่อกลิ้งไปมาบนพื้นผิว คุณเพิ่มภาพเคลื่อนไหวประเภทนี้ได้ด้วยแท็ก OnSwipe
ใน Transition
- แทนที่ TODO เพื่อเพิ่มแท็ก
OnSwipe
ด้วย<OnSwipe motion:touchAnchorId="@id/red_star" />
step2.xml
<!-- TODO add OnSwipe tag -->
<!-- A transition describes an animation via start and end state -->
<Transition
motion:constraintSetStart="@+id/start"
motion:constraintSetEnd="@+id/end">
<!-- MotionLayout will track swipes relative to this view -->
<OnSwipe motion:touchAnchorId="@id/red_star" />
</Transition>
OnSwipe
มีแอตทริบิวต์ 2-3 รายการ แอตทริบิวต์ที่สำคัญที่สุดคือ touchAnchorId
touchAnchorId
เป็นมุมมองที่ติดตามซึ่งเลื่อนไปตามการแตะMotionLayout
จะทำให้มุมมองนี้อยู่ห่างจากนิ้วที่เลื่อนเท่ากับระยะเท่าๆ กันtouchAnchorSide
กำหนดว่าควรติดตามข้อมูลพร็อพเพอร์ตี้ด้านใด ซึ่งเป็นสิ่งสำคัญสำหรับมุมมองที่ปรับขนาด ไปตามเส้นทางที่ซับซ้อน หรือมีด้านหนึ่งที่เคลื่อนเร็วกว่าอีกด้านdragDirection
จะกำหนดทิศทางที่สำคัญสำหรับภาพเคลื่อนไหวนี้ (ขึ้น ลง ซ้าย หรือขวา)
เมื่อ MotionLayout
ฟังเหตุการณ์การลาก ระบบจะลงทะเบียน Listener ในมุมมอง MotionLayout
ไม่ใช่มุมมองที่ touchAnchorId
ระบุ เมื่อผู้ใช้เริ่มท่าทางสัมผัสที่ใดก็ตามบนหน้าจอ MotionLayout
จะรักษาระยะห่างระหว่างนิ้วผู้ใช้กับ touchAnchorSide
ของการแสดงผล touchAnchorId
ให้คงที่ ตัวอย่างเช่น หากแตะด้านที่ห่างจากจุดยึด 100dp จะทำให้ MotionLayout
รักษาระยะห่างจากนิ้วด้านนั้นไว้ที่ 100dp สำหรับภาพเคลื่อนไหวทั้งหมด
ลองเลย
- เรียกใช้แอปอีกครั้ง และเปิดหน้าจอขั้นตอนที่ 2 จากนั้นคุณจะเห็นภาพเคลื่อนไหว
- ลองพูดว่า "สะบัด" หรือปล่อยนิ้วออกไปครึ่งทางของภาพเคลื่อนไหวเพื่อดูว่า
MotionLayout
แสดงภาพเคลื่อนไหวตามหลักฟิสิกส์แบบไหลอย่างไร
MotionLayout
สามารถสร้างภาพเคลื่อนไหวระหว่างการออกแบบที่แตกต่างกันอย่างมากโดยใช้ฟีเจอร์จาก ConstraintLayout
เพื่อสร้างเอฟเฟกต์ที่มีสีสัน
ในภาพเคลื่อนไหวนี้ มุมมองทั้ง 3 มุมมองจะมีตำแหน่งสัมพันธ์กับผู้ปกครองที่ด้านล่างของหน้าจอเพื่อเริ่มต้น ในตอนท้าย มุมมองทั้ง 3 จะมีตำแหน่งเทียบกับ @id/credits
ในเชน
แม้ว่าเลย์เอาต์จะต่างกันมาก แต่ MotionLayout
จะสร้างภาพเคลื่อนไหวแบบไหลระหว่างจุดเริ่มต้นและจุดสิ้นสุดได้
5. การแก้ไขเส้นทาง
ในขั้นตอนนี้ คุณจะได้สร้างภาพเคลื่อนไหวไปตามเส้นทางที่ซับซ้อนระหว่างภาพเคลื่อนไหว และทำให้เครดิตเคลื่อนไหวในระหว่างการเคลื่อนไหว MotionLayout
สามารถแก้ไขเส้นทางที่มุมมองจะใช้ระหว่างจุดเริ่มต้นและจุดสิ้นสุดได้โดยใช้ KeyPosition
ขั้นตอนที่ 1: สำรวจโค้ดที่มีอยู่
- เปิด
layout/activity_step3.xml
และxml/step3.xml
เพื่อดูเลย์เอาต์และฉากเคลื่อนไหวที่มีอยู่ImageView
และTextView
แสดงข้อความเกี่ยวกับดวงจันทร์และเครดิต - เปิดไฟล์ฉากเคลื่อนไหว (
xml/step3.xml
) คุณจะเห็นว่ามีการกำหนดTransition
ตั้งแต่@id/start
ถึง@id/end
ภาพเคลื่อนไหวจะย้ายรูปภาพดวงจันทร์จากมุมซ้ายล่างของหน้าจอไปที่ด้านขวาล่างของหน้าจอโดยใช้ConstraintSets
2 ตัว ข้อความเครดิตจะค่อยๆ ปรากฏขึ้นจากalpha="0.0"
เป็นalpha="1.0"
ขณะที่ดวงจันทร์กำลังเคลื่อนที่ - เรียกใช้แอปทันที และเลือกขั้นตอนที่ 3 คุณจะเห็นว่าดวงจันทร์เคลื่อนที่ตามเส้นทางเชิงเส้น (หรือเส้นตรง) ตั้งแต่เริ่มต้นจนจบเมื่อคุณคลิกที่ดวงจันทร์
ขั้นตอนที่ 2: เปิดใช้การแก้ไขข้อบกพร่องของเส้นทาง
ก่อนเพิ่มเส้นโค้งในการเคลื่อนที่ของดวงจันทร์ คุณควรเปิดใช้การแก้ไขข้อบกพร่องของเส้นทางใน MotionLayout
คุณสามารถวาดเส้นทางภาพเคลื่อนไหวของทุกมุมมองเพื่อช่วยพัฒนาภาพเคลื่อนไหวที่ซับซ้อนด้วย MotionLayout
ได้ ซึ่งจะเป็นประโยชน์เมื่อคุณต้องการแสดงภาพภาพเคลื่อนไหวและปรับแต่งรายละเอียดเล็กๆ น้อยๆ ของการเคลื่อนไหว
- หากต้องการเปิดใช้เส้นทางการแก้ไขข้อบกพร่อง ให้เปิด
layout/activity_step3.xml
แล้วเพิ่มmotion:motionDebug="SHOW_PATH"
ลงในแท็กMotionLayout
activity_step3.xml
<!-- Add motion:motionDebug="SHOW_PATH" -->
<androidx.constraintlayout.motion.widget.MotionLayout
...
motion:motionDebug="SHOW_PATH" >
หลังจากเปิดใช้การแก้ไขข้อบกพร่องของเส้นทางแล้ว เมื่อคุณเรียกใช้แอปอีกครั้ง คุณจะเห็นเส้นทางของมุมมองทั้งหมดแสดงเป็นเส้นประ
- วงกลมแสดงตำแหน่งเริ่มต้นหรือจุดสิ้นสุดของมุมมองหนึ่ง
- เส้นแสดงเส้นทางของมุมมองหนึ่ง
- Diamonds แสดงถึง
KeyPosition
ที่เปลี่ยนแปลงเส้นทาง
ตัวอย่างเช่น ในภาพเคลื่อนไหวนี้ วงกลมตรงกลางคือตำแหน่งของข้อความเครดิต
ขั้นตอนที่ 3: แก้ไขเส้นทาง
ภาพเคลื่อนไหวทั้งหมดใน MotionLayout
กำหนดโดยจุดเริ่มต้นและสิ้นสุด ConstraintSet
ซึ่งกำหนดลักษณะของหน้าจอก่อนที่ภาพเคลื่อนไหวจะเริ่มต้นและหลังจากเล่นภาพเคลื่อนไหวเสร็จแล้ว โดยค่าเริ่มต้น MotionLayout
จะพล็อตเส้นทางเชิงเส้น (เส้นตรง) ระหว่างตำแหน่งเริ่มต้นและสิ้นสุดของแต่ละมุมมองที่เปลี่ยนตำแหน่ง
ในการสร้างเส้นทางที่ซับซ้อน เช่น เส้นโค้งของดวงจันทร์ ในตัวอย่างนี้ MotionLayout
จะใช้ KeyPosition
เพื่อแก้ไขเส้นทางที่มุมมองใช้ระหว่างจุดเริ่มต้นและสิ้นสุด
- เปิด
xml/step3.xml
แล้วเพิ่มKeyPosition
ลงในฉาก แท็กKeyPosition
จะอยู่ภายในแท็กTransition
step3.xml
<!-- TODO: Add KeyFrameSet and KeyPosition -->
<KeyFrameSet>
<KeyPosition
motion:framePosition="50"
motion:motionTarget="@id/moon"
motion:keyPositionType="parentRelative"
motion:percentY="0.5"
/>
</KeyFrameSet>
KeyFrameSet
เป็นองค์ประกอบย่อยของ Transition
และเป็นชุดของ KeyFrames
ทั้งหมด เช่น KeyPosition
ที่ควรนำมาใช้ในช่วงการเปลี่ยนผ่าน
เนื่องจาก MotionLayout
กำลังคำนวณเส้นทางของดวงจันทร์ระหว่างจุดเริ่มต้นและจุดสิ้นสุด ดวงจันทร์จึงแก้ไขเส้นทางตาม KeyPosition
ที่ระบุไว้ใน KeyFrameSet
คุณสามารถดูว่าการดำเนินการนี้แก้ไขเส้นทางอย่างไรโดยเรียกใช้แอปอีกครั้ง
KeyPosition
มีแอตทริบิวต์หลายรายการที่อธิบายวิธีแก้ไขเส้นทาง ซึ่งปัจจัยที่สำคัญที่สุดมีดังนี้
framePosition
เป็นตัวเลขระหว่าง 0 ถึง 100 ซึ่งกำหนดเวลาที่ควรใช้KeyPosition
นี้ในภาพเคลื่อนไหว โดย 1 คือ 1% ถึงภาพเคลื่อนไหว และ 99 คือ 99% ผ่านภาพเคลื่อนไหว ดังนั้นหากค่าเป็น 50 ให้ใช้ค่าตรงกลางmotionTarget
คือข้อมูลพร็อพเพอร์ตี้ที่KeyPosition
นี้แก้ไขเส้นทางkeyPositionType
คือวิธีที่KeyPosition
นี้แก้ไขเส้นทาง ซึ่งจะเป็นparentRelative
,pathRelative
หรือdeltaRelative
ก็ได้ (ตามที่อธิบายไว้ในขั้นตอนถัดไป)percentX | percentY
คือจำนวนเงินที่จะแก้ไขเส้นทางที่framePosition
(ค่าระหว่าง 0.0 ถึง 1.0 โดยมีค่าลบและค่ามากกว่า 1)
คุณสามารถมองภาพในลักษณะนี้: "ที่ framePosition
ให้ปรับเส้นทางของ motionTarget
โดยเลื่อนเส้นทางโดย percentX
หรือ percentY
ตามพิกัดที่กำหนดโดย keyPositionType
"
โดยค่าเริ่มต้น MotionLayout
จะปัดเศษของมุมที่เกิดจากการแก้ไขเส้นทาง หากคุณดูภาพเคลื่อนไหวที่เพิ่งสร้างขึ้น คุณจะเห็นว่าดวงจันทร์เคลื่อนที่ตามเส้นทางโค้งที่ส่วนโค้ง สำหรับภาพเคลื่อนไหวส่วนใหญ่ นี่คือสิ่งที่คุณต้องการ แต่หากไม่ต้องการ คุณจะระบุแอตทริบิวต์ curveFit
เพื่อปรับแต่งได้
ลองเลย
หากเรียกใช้แอปอีกครั้ง คุณจะเห็นภาพเคลื่อนไหวสำหรับขั้นตอนนี้
ดวงจันทร์ตามแนวโค้งเนื่องจากวิ่งผ่าน KeyPosition
ที่ระบุไว้ใน Transition
<KeyPosition
motion:framePosition="50"
motion:motionTarget="@id/moon"
motion:keyPositionType="parentRelative"
motion:percentY="0.5"
/>
คุณสามารถอ่าน KeyPosition
นี้ว่า: "ที่ framePosition 50
(เมื่อภาพเคลื่อนไหวครึ่งหนึ่ง) ให้แก้ไขเส้นทางของ motionTarget
@id/moon
โดยเลื่อนเส้นทางไปทาง 50% Y
(เลื่อนลงไปครึ่งหนึ่งของหน้าจอ) ตามพิกัดที่กำหนดโดย parentRelative
(MotionLayout
ทั้งหน้า)"
ดังนั้น ในครึ่งทางของภาพเคลื่อนไหว ดวงจันทร์ต้องผ่าน KeyPosition
ลงมา 50% บนหน้าจอ KeyPosition
นี้ไม่ปรับการเคลื่อนที่แกน X เลย ดวงจันทร์จึงจะยังเคลื่อนที่จากจุดเริ่มต้นจนถึงจุดสิ้นสุดในแนวนอน MotionLayout
จะหาเส้นทางที่ราบรื่นซึ่งผ่าน KeyPosition
นี้ขณะที่สลับไปมาระหว่างจุดเริ่มต้นและสิ้นสุด
ถ้าดูดีๆ จะเห็นว่าข้อความเครดิตถูกจำกัดตามตำแหน่งของดวงจันทร์ ทำไมไม่ขยับในแนวตั้งด้วย
<Constraint
android:id="@id/credits"
...
motion:layout_constraintBottom_toBottomOf="@id/moon"
motion:layout_constraintTop_toTopOf="@id/moon"
/>
ปรากฏว่า แม้ว่าคุณจะแก้ไขเส้นทางที่ดวงจันทร์ใช้ แต่ตำแหน่งเริ่มต้นและสิ้นสุดของดวงจันทร์ก็ไม่เคลื่อนในแนวตั้งเลย KeyPosition
จะไม่ปรับเปลี่ยนตำแหน่งเริ่มต้นหรือตำแหน่งสิ้นสุด ข้อความเครดิตจึงถูกจำกัดไว้ที่ตำแหน่งสิ้นสุดสุดท้ายของดวงจันทร์
หากต้องการให้เครดิตย้ายไปตามดวงจันทร์ ให้เพิ่ม KeyPosition
ลงในเครดิต หรือแก้ไขข้อจำกัดเริ่มต้นใน @id/credits
ในส่วนถัดไปเราจะเจาะลึกเกี่ยวกับ keyPositionType
ประเภทต่างๆ ใน MotionLayout
6. ทำความเข้าใจ keyPositionType
ในขั้นตอนสุดท้าย คุณใช้ประเภท keyPosition
ของ parentRelative
เพื่อชดเชยเส้นทางเป็น 50% ของหน้าจอ แอตทริบิวต์ keyPositionType
จะกำหนดวิธีการ MotionLayout จะแก้ไขเส้นทางตาม percentX
หรือ percentY
<KeyFrameSet>
<KeyPosition
motion:framePosition="50"
motion:motionTarget="@id/moon"
motion:keyPositionType="parentRelative"
motion:percentY="0.5"
/>
</KeyFrameSet>
keyPosition
มี 3 ประเภทที่เป็นไปได้ ได้แก่ parentRelative
, pathRelative
และ deltaRelative
การระบุประเภทจะเปลี่ยนระบบพิกัดที่ใช้คำนวณ percentX
และ percentY
ระบบพิกัดคืออะไร
ระบบพิกัดเป็นวิธีระบุจุดในอวกาศ และมีประโยชน์ในการอธิบายตำแหน่งบนหน้าจอ
ระบบพิกัด MotionLayout
เป็นระบบพิกัดคาร์ทีเซีย ซึ่งหมายความว่ามีแกน X และ Y ที่กำหนดด้วยเส้นตั้งฉาก 2 เส้น ความแตกต่างสำคัญระหว่างแกน X คือตำแหน่งบนหน้าจอที่แกน X ไป (แกน Y จะตั้งฉากกับแกน X เสมอ)
ระบบพิกัดทั้งหมดใน MotionLayout
จะใช้ค่าระหว่าง 0.0
ถึง 1.0
ทั้งบนแกน X และ Y สามารถมีค่าติดลบและค่าที่มากกว่า 1.0
ได้ เช่น ค่า percentX
ของ -2.0
หมายความว่า ให้ไปในทิศทางตรงกันข้ามกับแกน X 2 ครั้ง
ถ้าฟังดูแล้วคล้ายกับคลาสพีชคณิตมากกว่า ก็ลองดูรูปภาพด้านล่างนี้ได้เลย
พิกัดระดับบนสุด
keyPositionType
ของ parentRelative
ใช้ระบบพิกัดเดียวกันกับหน้าจอ ซึ่งจะกำหนด (0, 0)
ที่ด้านซ้ายบนของ MotionLayout
ทั้งหมด และ (1, 1)
ที่ด้านล่างขวา
คุณสามารถใช้ parentRelative
ได้ทุกเมื่อที่ต้องการสร้างภาพเคลื่อนไหวที่เคลื่อนผ่าน MotionLayout
ทั้งหมด เช่น รูปโค้งดวงจันทร์ในตัวอย่างนี้
อย่างไรก็ตาม ถ้าคุณต้องการแก้ไขเส้นทางที่สัมพันธ์กับการเคลื่อนไหว เช่น ทำให้โค้งเล็กน้อย ระบบพิกัดอีกสองระบบเป็นทางเลือกที่ดีกว่า
พิกัดเดลต้าสัมพัทธ์
Delta เป็นคำศัพท์ทางคณิตศาสตร์สำหรับการเปลี่ยนแปลง ดังนั้น deltaRelative
จึงเป็นวิธีในการสอนว่า "การเปลี่ยนแปลงสัมพัทธ์" ในพิกัด deltaRelative
(0,0)
คือตำแหน่งเริ่มต้นของมุมมอง และ (1,1)
คือตำแหน่งสิ้นสุด แกน X และ Y จะอยู่ในแนวเดียวกับหน้าจอ
แกน X จะอยู่ในแนวนอนเสมอบนหน้าจอ และแกน Y จะอยู่ในหน้าจอแนวตั้งเสมอ เมื่อเทียบกับ parentRelative
ความแตกต่างที่สำคัญคือพิกัดจะอธิบายเฉพาะส่วนของหน้าจอที่มุมมองจะเคลื่อนไหว
deltaRelative
เป็นระบบพิกัดที่ยอดเยี่ยมในการควบคุมการเคลื่อนที่ในแนวนอนหรือแนวตั้งโดยลำพัง ตัวอย่างเช่น คุณสามารถสร้างภาพเคลื่อนไหวที่มีการเคลื่อนไหวในแนวตั้ง (Y) เพียง 50% และสร้างภาพเคลื่อนไหวต่อไปในแนวนอน (X)
พิกัด pathRelative
ระบบพิกัดสุดท้ายใน MotionLayout
คือ pathRelative
ซึ่งจะค่อนข้างแตกต่างจากอีก 2 รูปเนื่องจากแกน X จะตามเส้นทางการเคลื่อนไหวจากจุดเริ่มต้นถึงจุดสิ้นสุด ดังนั้น (0,0)
คือตำแหน่งเริ่มต้น และ (1,0)
คือตำแหน่งสิ้นสุด
ทำไมคุณถึงต้องการสิ่งนี้ ในครั้งแรกที่เห็น ค่อนข้างน่าประหลาดใจ โดยเฉพาะเมื่อระบบพิกัดนี้ไม่สอดคล้องกับระบบพิกัดหน้าจอ
ดูเหมือนว่า pathRelative
จะมีประโยชน์มากในบางเรื่อง
- การเร่ง ลดความเร็ว หรือหยุดมุมมองระหว่างส่วนหนึ่งของภาพเคลื่อนไหว เนื่องจากมิติข้อมูล X จะตรงกับเส้นทางที่มุมมองใช้ทุกประการเสมอ คุณจึงสามารถใช้
pathRelative
KeyPosition
เพื่อเปลี่ยนแปลงframePosition
จุดใดจุดหนึ่งในเส้นทางดังกล่าว ดังนั้นKeyPosition
ที่framePosition="50"
ที่มีpercentX="0.1"
จะทำให้ภาพเคลื่อนไหวใช้เวลา 50% ในการเดินทาง 10% แรกของการเคลื่อนไหว - การเพิ่มเส้นโค้งเล็กๆ น้อยๆ ลงในเส้นทาง เนื่องจากมิติ Y จะตั้งฉากกับการเคลื่อนไหวเสมอ การเปลี่ยน Y จะเปลี่ยนเส้นทางเป็นเส้นโค้งเมื่อเทียบกับการเคลื่อนไหวโดยรวม
- การเพิ่มมิติข้อมูลที่ 2 เมื่อ
deltaRelative
ไม่ทำงาน สำหรับการเคลื่อนไหวแนวตั้งและแนวนอนทั้งหมดdeltaRelative
จะสร้างมิติข้อมูลที่เป็นประโยชน์เพียง 1 รายการ อย่างไรก็ตามpathRelative
จะสร้างพิกัด X และ Y ที่ใช้ได้เสมอ
ในขั้นตอนถัดไป คุณจะได้เรียนรู้วิธีสร้างเส้นทางที่ซับซ้อนยิ่งขึ้นโดยใช้ KeyPosition
มากกว่า 1 รายการ
7. การสร้างเส้นทางที่ซับซ้อน
เมื่อดูภาพเคลื่อนไหวที่คุณสร้างในขั้นตอนสุดท้าย จะทำให้เกิดเส้นโค้งที่ราบรื่น แต่รูปทรงอาจเป็น "คล้ายดวงจันทร์" มากกว่า
แก้ไขเส้นทางที่มีองค์ประกอบ KeyPosition หลายรายการ
MotionLayout
สามารถแก้ไขเส้นทางเพิ่มเติมได้ด้วยการกำหนด KeyPosition
ได้มากเท่าที่ต้องการเพื่อให้เกิดการเคลื่อนไหว สำหรับภาพเคลื่อนไหวนี้ คุณจะสร้างเส้นโค้ง แต่คุณสามารถทำให้ดวงจันทร์พุ่งขึ้นและลงตรงกลางหน้าจอได้ หากต้องการ
- เปิด
xml/step4.xml
คุณจะเห็นว่ามีการดูเหมือนกับและมีKeyFrame
ที่เพิ่มไว้ในขั้นตอนสุดท้าย - หากต้องการปัดเศษของเส้นโค้ง เพิ่ม
KeyPositions
อีก 2 รายการลงในเส้นทางของ@id/moon
โดย 1 เส้นก่อนที่จะถึงจุดสูงสุด และอีก 1 เส้นหลังจากนั้น
step4.xml
<!-- TODO: Add two more KeyPositions to the KeyFrameSet here -->
<KeyPosition
motion:framePosition="25"
motion:motionTarget="@id/moon"
motion:keyPositionType="parentRelative"
motion:percentY="0.6"
/>
<KeyPosition
motion:framePosition="75"
motion:motionTarget="@id/moon"
motion:keyPositionType="parentRelative"
motion:percentY="0.6"
/>
ระบบจะใช้ KeyPositions
เป็น 25% และ 75% ของภาพเคลื่อนไหวทั้งหมด และทำให้ @id/moon
เคลื่อนที่ไปตามเส้นทางที่ห่างจากด้านบนของหน้าจอ 60% เมื่อรวมกับ KeyPosition
ที่มีอยู่ที่ 50% จะทำให้เกิดเส้นโค้งที่ราบเรียบขึ้นเพื่อให้ดวงจันทร์เคลื่อนที่ตามมา
ใน MotionLayout
คุณจะเพิ่ม KeyPositions
ได้มากเท่าที่ต้องการเพื่อให้ได้เส้นทางการเคลื่อนไหวที่ต้องการ MotionLayout
จะใช้แต่ละ KeyPosition
ตาม framePosition
ที่ระบุ และหาวิธีสร้างภาพเคลื่อนไหวที่ลื่นไหลตลอดทั้งKeyPositions
ลองเลย
- เรียกใช้แอปอีกครั้ง ไปที่ขั้นตอนที่ 4 เพื่อดูการทำงานของภาพเคลื่อนไหว เมื่อคุณคลิกดวงจันทร์ ดวงจันทร์จะตามเส้นทางตั้งแต่ต้นจนจบ โดยจะผ่าน
KeyPosition
แต่ละรายการที่ระบุไว้ในKeyFrameSet
สำรวจด้วยตนเอง
ก่อนจะเปลี่ยนไปใช้ KeyFrame
ประเภทอื่นๆ ให้ลองเพิ่ม KeyPositions
ใน KeyFrameSet
เพื่อดูว่าสร้างเอฟเฟกต์แบบใดได้บ้างโดยใช้ KeyPosition
นี่คือตัวอย่างหนึ่งที่แสดงวิธีสร้างเส้นทางที่ซับซ้อนซึ่งเคลื่อนที่กลับไปกลับมาระหว่างการสร้างภาพเคลื่อนไหว
step4.xml
<!-- Complex paths example: Dancing moon -->
<KeyFrameSet>
<KeyPosition
motion:framePosition="25"
motion:motionTarget="@id/moon"
motion:keyPositionType="parentRelative"
motion:percentY="0.6"
motion:percentX="0.1"
/>
<KeyPosition
motion:framePosition="50"
motion:motionTarget="@id/moon"
motion:keyPositionType="parentRelative"
motion:percentY="0.5"
motion:percentX="0.3"
/>
<KeyPosition
motion:framePosition="75"
motion:motionTarget="@id/moon"
motion:keyPositionType="parentRelative"
motion:percentY="0.6"
motion:percentX="0.1"
/>
</KeyFrameSet>
เมื่อคุณสำรวจ KeyPosition
เสร็จแล้ว คุณจะไปยัง KeyFrames
ประเภทอื่นๆ ในขั้นตอนถัดไป
8. การเปลี่ยนแอตทริบิวต์ระหว่างการเคลื่อนไหว
การสร้างภาพเคลื่อนไหวแบบไดนามิกมักจะหมายถึงการเปลี่ยนแปลงของมุมมอง size
, rotation
หรือ alpha
ในขณะที่ภาพเคลื่อนไหวดำเนินไป MotionLayout
รองรับการทำให้แอตทริบิวต์หลายรายการเคลื่อนไหวในมุมมองใดก็ตามโดยใช้ KeyAttribute
ในขั้นตอนนี้ คุณจะใช้ KeyAttribute
เพื่อปรับสัดส่วนของดวงจันทร์และหมุน คุณยังต้องใช้ KeyAttribute
เพื่อชะลอการแสดงข้อความด้วย จนกว่าดวงจันทร์จะเดินทางใกล้เสร็จสมบูรณ์
ขั้นตอนที่ 1: ปรับขนาดและหมุนด้วย KeyAttribute
- เปิด
xml/step5.xml
ซึ่งมีภาพเคลื่อนไหวเดียวกันกับที่คุณสร้างในขั้นตอนสุดท้าย หน้าจอนี้ใช้ภาพพื้นที่อื่นเป็นพื้นหลังเพื่อความหลากหลาย - หากต้องการให้ดวงจันทร์ขยายขนาดและหมุน ให้เพิ่มแท็ก
KeyAttribute
2 แท็กในKeyFrameSet
ที่keyFrame="50"
และkeyFrame="100"
step5.xml
<!-- TODO: Add KeyAttributes to rotate and resize @id/moon -->
<KeyAttribute
motion:framePosition="50"
motion:motionTarget="@id/moon"
android:scaleY="2.0"
android:scaleX="2.0"
android:rotation="-360"
/>
<KeyAttribute
motion:framePosition="100"
motion:motionTarget="@id/moon"
android:rotation="-720"
/>
KeyAttributes
นี้จะใช้ที่ 50% และ 100% ของภาพเคลื่อนไหว KeyAttribute
แรกที่ 50% จะปรากฏที่ด้านบนของแนวโค้ง และทำให้มุมมองมีขนาดใหญ่ขึ้นเป็น 2 เท่า รวมทั้งหมุน -360 องศา (หรือวงกลมเต็ม 1 วง) KeyAttribute
รอบที่ 2 จะหมุนรอบที่ 2 จนเสร็จเป็น -720 องศา (วงกลมเต็ม 2 วง) และย่อขนาดกลับเป็นปกติเนื่องจากค่า scaleX
และ scaleY
มีค่าเริ่มต้นเป็น 1.0
เช่นเดียวกับ KeyPosition
KeyAttribute
จะใช้ framePosition
และ motionTarget
เพื่อระบุว่าเมื่อใดจะใช้ KeyFrame
และมุมมองใดที่จะแก้ไข MotionLayout
จะหาค่าประมาณระหว่าง KeyPositions
เพื่อสร้างภาพเคลื่อนไหวแบบไหล
KeyAttributes
รองรับแอตทริบิวต์ที่สามารถใช้กับข้อมูลพร็อพเพอร์ตี้ทั้งหมด โดยรองรับการเปลี่ยนแปลงแอตทริบิวต์พื้นฐาน เช่น visibility
, alpha
หรือ elevation
คุณยังสามารถเปลี่ยนการหมุนได้เช่นเดียวกับที่คุณทำที่นี่ หมุนแบบ 3 มิติด้วย rotateX
และ rotateY
ปรับขนาดด้วย scaleX
และ scaleY
หรือแปลตำแหน่งมุมมองเป็น X, Y หรือ Z
ขั้นตอนที่ 2: ชะลอการแสดงเครดิต
เป้าหมายอย่างหนึ่งของขั้นตอนนี้คือการอัปเดตภาพเคลื่อนไหวเพื่อไม่ให้ข้อความเครดิตปรากฏจนกว่าภาพเคลื่อนไหวจะเสร็จสมบูรณ์เป็นส่วนใหญ่
- หากต้องการชะลอการแสดงเครดิต ให้ระบุ
KeyAttribute
อีก 1 รายการเพื่อให้alpha
มีค่าเป็น 0 จนถึงkeyPosition="85"
MotionLayout
จะยังคงเปลี่ยนจาก 0 เป็น 100 Alpha ได้อย่างราบรื่น แต่จะเปลี่ยนในช่วง 15% สุดท้ายของภาพเคลื่อนไหว
step5.xml
<!-- TODO: Add KeyAttribute to delay the appearance of @id/credits -->
<KeyAttribute
motion:framePosition="85"
motion:motionTarget="@id/credits"
android:alpha="0.0"
/>
KeyAttribute
นี้ทำให้ alpha
ของ @id/credits
เท่ากับ 0.0 สำหรับ 85% แรกของภาพเคลื่อนไหว และเนื่องจากเริ่มที่อัลฟ่าเป็น 0 ดังนั้นจะมองไม่เห็นใน 85% แรกของภาพเคลื่อนไหว
ผลลัพธ์สุดท้ายของ KeyAttribute
นี้คือเครดิตจะปรากฏที่ส่วนท้ายของภาพเคลื่อนไหว ซึ่งทำให้รูปวงกลมซ้อนอยู่กับดวงจันทร์ที่ตกกระทบมุมขวาของหน้าจอ
การหน่วงเวลาภาพเคลื่อนไหวในมุมมองหนึ่งขณะที่อีกมุมมองหนึ่งเคลื่อนไหวเช่นนี้ คุณสามารถสร้างภาพเคลื่อนไหวที่น่าประทับใจที่ทำให้ผู้ใช้รู้สึกดูมีชีวิตชีวา
ลองเลย
- เรียกใช้แอปอีกครั้ง แล้วไปที่ขั้นตอนที่ 5 เพื่อดูการทำงานของภาพเคลื่อนไหว เมื่อคุณคลิกดวงจันทร์ ดวงจันทร์จะขับไปตามเส้นทางตั้งแต่ต้นจนจบ โดยจะผ่าน
KeyAttribute
แต่ละจุดที่ระบุไว้ในKeyFrameSet
เนื่องจากคุณหมุนดวงจันทร์เต็มดวง 2 วง คราวนี้จึงจะหมุนกลับ 2 รอบ และเครดิตจะหน่วงเวลาลักษณะที่ปรากฏจนกว่าภาพเคลื่อนไหวจะเสร็จสมบูรณ์
สำรวจด้วยตนเอง
ก่อนที่จะดำเนินการต่อไปยังประเภท KeyFrame
ขั้นสุดท้าย ให้ลองแก้ไขแอตทริบิวต์มาตรฐานอื่นๆ ใน KeyAttributes
เช่น ลองเปลี่ยน rotation
เป็น rotationX
เพื่อดูว่าระบบสร้างภาพเคลื่อนไหวแบบใด
รายการแอตทริบิวต์มาตรฐานที่คุณลองใช้ได้มีดังนี้
android:visibility
android:alpha
android:elevation
android:rotation
android:rotationX
android:rotationY
android:scaleX
android:scaleY
android:translationX
android:translationY
android:translationZ
9. การเปลี่ยนแอตทริบิวต์ที่กำหนดเอง
ภาพเคลื่อนไหวที่สมบูรณ์เกี่ยวข้องกับการเปลี่ยนสีหรือแอตทริบิวต์อื่นๆ ของมุมมอง แม้ว่า MotionLayout
จะใช้ KeyAttribute
เพื่อเปลี่ยนแอตทริบิวต์มาตรฐานที่ระบุไว้ในงานก่อนหน้าได้ แต่คุณจะใช้ CustomAttribute
เพื่อระบุแอตทริบิวต์อื่นๆ ก็ได้
คุณสามารถใช้ CustomAttribute
เพื่อตั้งค่าใดๆ ที่มีตัวตั้งค่า เช่น คุณตั้งค่า backgroundColor ในมุมมองโดยใช้ CustomAttribute
ได้ MotionLayout
จะใช้การสะท้อนเพื่อค้นหาตัวตั้งค่า แล้วเรียกใช้ซ้ำๆ เพื่อให้มุมมองเคลื่อนไหว
ในขั้นตอนนี้ คุณจะใช้ CustomAttribute
ในการตั้งค่าแอตทริบิวต์ colorFilter
บนดวงจันทร์เพื่อสร้างภาพเคลื่อนไหวที่แสดงด้านล่าง
ระบุแอตทริบิวต์ที่กำหนดเอง
- ในการเริ่มต้นใช้งาน ให้เปิด
xml/step6.xml
ซึ่งมีภาพเคลื่อนไหวเดียวกันกับที่คุณสร้างไว้ในขั้นตอนสุดท้าย - หากต้องการทำให้ดวงจันทร์เปลี่ยนสี ให้เพิ่ม
KeyAttribute
จำนวน 2 รายการที่มีCustomAttribute
ในKeyFrameSet
ที่keyFrame="0"
,keyFrame="50"
และkeyFrame="100".
step6.xml
<!-- TODO: Add Custom attributes here -->
<KeyAttribute
motion:framePosition="0"
motion:motionTarget="@id/moon">
<CustomAttribute
motion:attributeName="colorFilter"
motion:customColorValue="#FFFFFF"
/>
</KeyAttribute>
<KeyAttribute
motion:framePosition="50"
motion:motionTarget="@id/moon">
<CustomAttribute
motion:attributeName="colorFilter"
motion:customColorValue="#FFB612"
/>
</KeyAttribute>
<KeyAttribute
motion:framePosition="100"
motion:motionTarget="@id/moon">
<CustomAttribute
motion:attributeName="colorFilter"
motion:customColorValue="#FFFFFF"
/>
</KeyAttribute>
คุณเพิ่ม CustomAttribute
ภายใน KeyAttribute
ระบบจะใช้ CustomAttribute
ตาม framePosition
ที่ระบุโดย KeyAttribute
ภายใน CustomAttribute
คุณต้องระบุ attributeName
และค่า 1 ค่าที่จะตั้งค่า
motion:attributeName
คือชื่อของตัวตั้งค่าที่แอตทริบิวต์ที่กำหนดเองนี้จะเรียกใช้ ในตัวอย่างนี้setColorFilter
ในDrawable
จะถูกเรียกmotion:custom*Value
คือค่าที่กำหนดเองสำหรับประเภทที่ระบุไว้ในชื่อ ในตัวอย่างนี้ค่าที่กำหนดเองจะเป็นสีที่ระบุ
ค่าที่กำหนดเองมีประเภทต่อไปนี้ได้
- สี
- จำนวนเต็ม
- ทศนิยม
- สตริง
- มิติข้อมูล
- บูลีน
เมื่อใช้ API นี้ MotionLayout
สามารถทำให้ทุกอย่างที่เป็นตัวตั้งค่าภาพเคลื่อนไหวได้
ลองเลย
- เรียกใช้แอปอีกครั้ง แล้วไปที่ขั้นตอนที่ 6 เพื่อดูการทำงานของภาพเคลื่อนไหว เมื่อคุณคลิกดวงจันทร์ ดวงจันทร์จะขับไปตามเส้นทางตั้งแต่ต้นจนจบ โดยจะผ่าน
KeyAttribute
แต่ละจุดที่ระบุไว้ในKeyFrameSet
เมื่อคุณเพิ่ม KeyFrames
อีก MotionLayout
จะเปลี่ยนเส้นทางของดวงจันทร์จากเส้นตรงเป็นเส้นโค้งที่ซับซ้อน เพิ่มการพลิกกลับ 2 ครั้ง ปรับขนาด และการเปลี่ยนสีในช่วงกลางของภาพเคลื่อนไหว
ในภาพเคลื่อนไหวจริง คุณมักจะสร้างภาพเคลื่อนไหวหลายมุมมองพร้อมๆ กัน ซึ่งควบคุมการเคลื่อนไหวของมุมมองไปตามเส้นทางและความเร็วที่ต่างกัน เมื่อระบุ KeyFrame
ที่แตกต่างกันสำหรับแต่ละมุมมอง คุณจะสามารถออกแบบภาพเคลื่อนไหวที่สมบูรณ์ซึ่งจะทำให้หลายมุมมองเคลื่อนไหวได้ด้วย MotionLayout
10. ลากเหตุการณ์และเส้นทางที่ซับซ้อน
ในขั้นตอนนี้ คุณจะได้สำรวจโดยใช้ OnSwipe
ซึ่งมีเส้นทางที่ซับซ้อน จนถึงตอนนี้ ผู้ฟัง OnClick
ได้ทริกเกอร์ภาพเคลื่อนไหวของดวงจันทร์แล้ว และจะแสดงตามระยะเวลาที่กำหนด
การควบคุมภาพเคลื่อนไหวที่มีเส้นทางที่ซับซ้อนโดยใช้ OnSwipe
เช่น ภาพเคลื่อนไหวของดวงจันทร์ที่สร้างขึ้นในช่วง 2-3 ขั้นตอนที่แล้วต้องเข้าใจวิธีการทำงานของ OnSwipe
ขั้นตอนที่ 1: สำรวจลักษณะการปัดเมื่อ
- เปิด
xml/step7.xml
และค้นหาการประกาศOnSwipe
ที่มีอยู่
step7.xml
<!-- Fix OnSwipe by changing touchAnchorSide →
<OnSwipe
motion:touchAnchorId="@id/moon"
motion:touchAnchorSide="bottom"
/>
- เรียกใช้แอปในอุปกรณ์ และไปที่ขั้นตอนที่ 7 ดูว่าคุณสามารถสร้างภาพเคลื่อนไหวที่ราบรื่นด้วยการลากดวงจันทร์ไปตามเส้นทางโค้งหรือไม่
เมื่อคุณเรียกใช้ภาพเคลื่อนไหวนี้ ภาพจะดูไม่ดีนัก หลังจากที่ดวงจันทร์ขึ้นถึงจุดสูงสุดของกราฟ จะเริ่มหมุนไปรอบๆ
ในการทำความเข้าใจข้อบกพร่อง ให้พิจารณาสิ่งที่เกิดขึ้นเมื่อผู้ใช้แตะที่ส่วนล่างของส่วนโค้ง เนื่องจากแท็ก OnSwipe
มี motion:touchAnchorSide="bottom"
MotionLayout
จะพยายามทำให้ระยะห่างระหว่างนิ้วกับด้านล่างของมุมมองคงที่ตลอดภาพเคลื่อนไหว
แต่เนื่องจากด้านล่างของดวงจันทร์อาจไม่ได้ไปในทิศทางเดียวกันเสมอไป ดวงจันทร์ลอยขึ้นแล้วกลับลงด้านล่าง MotionLayout
จึงไม่รู้ว่าจะต้องทำอย่างไรเมื่อผู้ใช้เพิ่งผ่านแนวโค้งด้านบน เนื่องจากคุณกำลังติดตามด้านล่างของดวงจันทร์ ควรวางดวงจันทร์ไว้ที่ใดเมื่อผู้ใช้แตะที่นี่
ขั้นตอนที่ 2: ใช้ด้านขวา
เพื่อหลีกเลี่ยงข้อบกพร่องเช่นนี้ สิ่งสำคัญคือการเลือก touchAnchorId
และ touchAnchorSide
ที่มีความคืบหน้าในทิศทางเดียวตลอดระยะเวลาของภาพเคลื่อนไหวทั้งหมดเสมอ
ในภาพเคลื่อนไหวนี้ ทั้งด้านright
และด้านleft
ของดวงจันทร์จะเคลื่อนผ่านหน้าจอไปในทิศทางเดียว
อย่างไรก็ตาม ทั้ง bottom
และ top
จะกลับทิศทาง เมื่อ OnSwipe
พยายามติดตามตัว จะสับสนเมื่อทิศทางเปลี่ยนไป
- หากต้องการให้ภาพเคลื่อนไหวนี้เกิดขึ้นหลังเหตุการณ์การแตะ ให้เปลี่ยน
touchAnchorSide
เป็นright
step7.xml
<!-- Fix OnSwipe by changing touchAnchorSide →
<OnSwipe
motion:touchAnchorId="@id/moon"
motion:touchAnchorSide="right"
/>
ขั้นตอนที่ 3: ใช้ DragDirection
คุณยังรวม dragDirection
กับ touchAnchorSide
เพื่อทำให้แทร็กด้านข้างมีทิศทางต่างจากปกติได้ด้วย ยังเป็นสิ่งสำคัญที่ touchAnchorSide
จะเดินหน้าไปในทิศทางเดียวเท่านั้น แต่คุณบอก MotionLayout
ได้ว่าจะต้องติดตามทิศทางใด ตัวอย่างเช่น คุณสามารถเก็บ touchAnchorSide="bottom"
ไว้ แต่เพิ่ม dragDirection="dragRight"
ซึ่งจะทำให้ MotionLayout
ติดตามตำแหน่งด้านล่างของมุมมอง แต่จะพิจารณาเฉพาะตำแหน่งเมื่อเลื่อนไปทางขวาเท่านั้น (ไม่พิจารณาการเคลื่อนไหวในแนวตั้ง) ดังนั้นแม้ว่าด้านล่างขึ้นและลง แต่จะยังเคลื่อนไหวอย่างถูกต้องด้วย OnSwipe
- อัปเดต
OnSwipe
เพื่อติดตามการเคลื่อนที่ของดวงจันทร์อย่างถูกต้อง
step7.xml
<!-- Using dragDirection to control the direction of drag tracking →
<OnSwipe
motion:touchAnchorId="@id/moon"
motion:touchAnchorSide="bottom"
motion:dragDirection="dragRight"
/>
ลองเลย
- เรียกใช้แอปอีกครั้งและลองลากดวงจันทร์ผ่านเส้นทางทั้งหมด แม้ว่าจะหมุนตามแนวโค้งที่ซับซ้อน แต่
MotionLayout
จะสามารถทำให้ภาพเคลื่อนไหวเล่นต่อไปเรื่อยๆ ตามเหตุการณ์การเลื่อนได้
11. ภาพเคลื่อนไหวที่ทำงานอยู่เมื่อใช้โค้ด
คุณสามารถใช้ MotionLayout
เพื่อสร้างภาพเคลื่อนไหวที่สมบูรณ์เมื่อใช้กับ CoordinatorLayout
ในขั้นตอนนี้ คุณจะสร้างส่วนหัวที่ยุบได้โดยใช้ MotionLayout
ขั้นตอนที่ 1: สำรวจโค้ดที่มีอยู่
- เปิด
layout/activity_step8.xml
เพื่อเริ่มต้นใช้งาน - ใน
layout/activity_step8.xml
คุณเห็นว่ามีการสร้างCoordinatorLayout
และAppBarLayout
ที่ใช้งานได้แล้ว
activity_step8.xml
<androidx.coordinatorlayout.widget.CoordinatorLayout
...>
<com.google.android.material.appbar.AppBarLayout
android:id="@+id/appbar_layout"
android:layout_width="match_parent"
android:layout_height="180dp">
<androidx.constraintlayout.motion.widget.MotionLayout
android:id="@+id/motion_layout"
... >
...
</androidx.constraintlayout.motion.widget.MotionLayout>
</com.google.android.material.appbar.AppBarLayout>
<androidx.core.widget.NestedScrollView
...
motion:layout_behavior="@string/appbar_scrolling_view_behavior" >
...
</androidx.core.widget.NestedScrollView>
</androidx.coordinatorlayout.widget.CoordinatorLayout>
เลย์เอาต์นี้ใช้ CoordinatorLayout
เพื่อแชร์ข้อมูลการเลื่อนระหว่าง NestedScrollView
และ AppBarLayout
ดังนั้น เมื่อ NestedScrollView
เลื่อนขึ้นก็จะบอก AppBarLayout
เกี่ยวกับการเปลี่ยนแปลง นี่คือวิธีการใช้งานการยุบแถบเครื่องมือแบบนี้ใน Android โดยที่การเลื่อนข้อความจะเป็นแบบ "ประสานกัน" ด้วยส่วนหัวการย่อ
ฉากเคลื่อนไหวที่ @id/motion_layout
ชี้ไปจะคล้ายกับฉากเคลื่อนไหวในขั้นตอนสุดท้าย อย่างไรก็ตาม ระบบนำการประกาศ OnSwipe
ออกเพื่อให้ทำงานร่วมกับ CoordinatorLayout
ได้
- เรียกใช้แอป แล้วไปที่ขั้นตอนที่ 8 คุณจะเห็นว่าเมื่อคุณเลื่อนข้อความ ดวงจันทร์ไม่เคลื่อนที่
ขั้นตอนที่ 2: ทำให้ MotionLayout เลื่อน
- หากต้องการทำให้มุมมอง
MotionLayout
เลื่อนทันทีที่NestedScrollView
เลื่อน ให้เพิ่มmotion:minHeight
และmotion:layout_scrollFlags
ลงในMotionLayout
activity_step8.xml
<!-- Add minHeight and layout_scrollFlags to the MotionLayout -->
<androidx.constraintlayout.motion.widget.MotionLayout
android:id="@+id/motion_layout"
android:layout_width="match_parent"
android:layout_height="match_parent"
motion:layoutDescription="@xml/step8"
motion:motionDebug="SHOW_PATH"
android:minHeight="80dp"
motion:layout_scrollFlags="scroll|enterAlways|snap|exitUntilCollapsed" >
- เรียกใช้แอปอีกครั้ง แล้วไปที่ขั้นตอนที่ 8 คุณจะเห็นว่า
MotionLayout
ยุบลงเมื่อคุณเลื่อนขึ้น แต่ภาพเคลื่อนไหวจะยังไม่คืบหน้าตามลักษณะการเลื่อน
ขั้นตอนที่ 3: ย้ายการเคลื่อนไหวด้วยโค้ด
- เปิด
Step8Activity.kt
แก้ไขฟังก์ชันcoordinateMotion()
เพื่อแจ้งMotionLayout
เกี่ยวกับการเปลี่ยนแปลงตำแหน่งการเลื่อน
Step8Activity.kt
// TODO: set progress of MotionLayout based on an AppBarLayout.OnOffsetChangedListener
private fun coordinateMotion() {
val appBarLayout: AppBarLayout = findViewById(R.id.appbar_layout)
val motionLayout: MotionLayout = findViewById(R.id.motion_layout)
val listener = AppBarLayout.OnOffsetChangedListener { unused, verticalOffset ->
val seekPosition = -verticalOffset / appBarLayout.totalScrollRange.toFloat()
motionLayout.progress = seekPosition
}
appBarLayout.addOnOffsetChangedListener(listener)
}
โค้ดนี้จะบันทึก OnOffsetChangedListener
ซึ่งจะเรียกใช้ทุกครั้งที่ผู้ใช้เลื่อนด้วยค่าออฟเซ็ตการเลื่อนปัจจุบัน
MotionLayout
รองรับการกรอการเปลี่ยนด้วยการตั้งค่าพร็อพเพอร์ตี้ความคืบหน้า หากต้องการแปลงความคืบหน้าระหว่าง verticalOffset
กับเปอร์เซ็นต์ ให้หารด้วยช่วงการเลื่อนทั้งหมด
ลองเลย
- ทำให้แอปใช้งานได้อีกครั้งและเรียกใช้ภาพเคลื่อนไหวขั้นตอนที่ 8 คุณจะเห็นว่า
MotionLayout
จะเลื่อนภาพเคลื่อนไหวตามตำแหน่งการเลื่อน
คุณสามารถสร้างภาพเคลื่อนไหวของแถบเครื่องมือการยุบแบบไดนามิกที่กำหนดเองได้โดยใช้ MotionLayout
เมื่อใช้ KeyFrames
ต่อเนื่อง คุณจะได้ผลลัพธ์ที่ชัดเจนมาก
12. ขอแสดงความยินดี
Codelab นี้ครอบคลุม API พื้นฐานของ MotionLayout
ดูตัวอย่างเพิ่มเติมของ MotionLayout
ในทางปฏิบัติได้ที่ตัวอย่างอย่างเป็นทางการ และอย่าลืมอ่านเอกสารประกอบ
ดูข้อมูลเพิ่มเติม
MotionLayout
รองรับฟีเจอร์เพิ่มเติมที่ไม่ครอบคลุมใน Codelab เช่น KeyCycle,
ซึ่งให้คุณควบคุมเส้นทางหรือแอตทริบิวต์ที่มีรอบการทำงานซ้ำได้ และ KeyTimeCycle,
ที่ช่วยให้คุณเคลื่อนไหวตามเวลาของนาฬิกาได้ ดูตัวอย่างของแต่ละวิธี
สําหรับลิงก์ไปยัง Codelab อื่นๆ ในหลักสูตรนี้ โปรดดูหน้า Landing Page สำหรับ Android ขั้นสูงใน Kotlin