Android ขั้นสูงใน Kotlin 03.2: ภาพเคลื่อนไหวพร้อม MotionLayout

codelab นี้เป็นส่วนหนึ่งของหลักสูตร Advanced Android ใน Kotlin คุณจะได้รับประโยชน์สูงสุดจากหลักสูตรนี้หากคุณทำงานผ่าน codelabs ตามลำดับ แต่ไม่ได้บังคับ โค้ดแล็บของหลักสูตรทั้งหมดจะแสดงรายการอยู่ ในหน้า Landing Page ของแท็บการเข้ารหัสของ Kotlin ขั้นสูงของ Android

MotionLayout เป็นไลบรารีที่ให้คุณเพิ่มการเคลื่อนไหวที่สมบูรณ์ลงในแอป Android ของคุณ มันขึ้นอยู่กับ ConstraintLayout, และให้คุณเคลื่อนไหวทุกอย่างที่คุณสามารถสร้างได้โดยใช้ ConstraintLayout

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

ภาพเคลื่อนไหวเป็นวิธีที่ยอดเยี่ยมในการปรับปรุงประสบการณ์การใช้งานแอป คุณสามารถใช้ภาพเคลื่อนไหวเพื่อ:

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

ข้อกำหนดเบื้องต้น

codelab นี้ได้รับการออกแบบมาสำหรับนักพัฒนาที่มีประสบการณ์ในการพัฒนา Android ก่อนที่จะพยายามกรอก codelab นี้คุณควร:

  • เรียนรู้วิธีสร้างแอปด้วยกิจกรรมเค้าโครงพื้นฐานและเรียกใช้บนอุปกรณ์หรือโปรแกรมจำลองโดยใช้ Android Studio คุ้นเคยกับ ConstraintLayout อ่าน โค้ดแล็บเค้าโครงข้อ จำกัด เพื่อเรียนรู้เพิ่มเติมเกี่ยวกับ ConstraintLayout

คุณจะทำอะไร

  • กำหนดภาพเคลื่อนไหวด้วย ConstraintSets และ MotionLayout
  • เคลื่อนไหวตามเหตุการณ์การลาก
  • เปลี่ยนภาพเคลื่อนไหวด้วย KeyPosition
  • เปลี่ยนแอตทริบิวต์ด้วย KeyAttribute
  • เรียกใช้ภาพเคลื่อนไหวด้วยรหัส
  • เคลื่อนไหวส่วนหัวที่ยุบได้ด้วย MotionLayout

สิ่งที่คุณต้องการ

  • Android Studio 4.0 (ตัวแก้ไข MotionLayout ใช้งานได้กับ Android Studio เวอร์ชันนี้เท่านั้น)

ในการดาวน์โหลดแอปตัวอย่างคุณสามารถ:

ดาวน์โหลด Zip

... หรือโคลนที่เก็บ GitHub จากบรรทัดคำสั่งโดยใช้คำสั่งต่อไปนี้:

$ git clone https://github.com/googlecodelabs/motionlayout.git

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

ในการสร้างแอนิเมชั่นจากโค้ดเริ่มต้นคุณจะต้องมีส่วนสำคัญดังต่อไปนี้

  • MotionLayout, ซึ่งเป็นคลาสย่อยของ ConstraintLayout คุณระบุมุมมองทั้งหมดที่จะเคลื่อนไหวภายในแท็ก MotionLayout
  • MotionScene, ซึ่งเป็นไฟล์ XML ที่อธิบายภาพเคลื่อนไหวสำหรับ MotionLayout.
  • การ Transition, ซึ่งเป็นส่วนหนึ่งของ MotionScene ที่ระบุระยะเวลาของภาพเคลื่อนไหวทริกเกอร์และวิธีย้ายมุมมอง
  • ConstraintSet ที่ระบุทั้ง ConstraintSet เริ่มต้น และ จุดสิ้นสุด ของการเปลี่ยนแปลง

ลองมาดูแต่ละสิ่งเหล่านี้โดยเริ่มจาก MotionLayout

ขั้นตอนที่ 1: สำรวจโค้ดที่มีอยู่

MotionLayout เป็นคลาสย่อยของ ConstraintLayout ดังนั้นจึงรองรับคุณสมบัติเดียวกันทั้งหมดในขณะที่เพิ่มแอนิเมชั่น ในการใช้ MotionLayout คุณต้องเพิ่มมุมมอง MotionLayout ที่คุณจะใช้ ConstraintLayout.

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

ขั้นตอนที่ 2: แปลงเป็นเค้าโครงแบบเคลื่อนไหว

ในการทำให้เคลื่อนไหวโดยใช้ MotionLayout, คุณต้องแปลง ConstraintLayout เป็น MotionLayout

เพื่อให้เค้าโครงของคุณใช้ฉากเคลื่อนไหวต้องชี้ไปที่ฉากนั้น

  1. ในการดำเนินการนี้ให้เปิดพื้นผิวการออกแบบ ใน Android Studio 4.0 คุณเปิดพื้นผิวการออกแบบโดยใช้ไอคอนแยกหรือออกแบบที่ด้านขวาบนเมื่อดูไฟล์ XML ของเค้าโครง

a2beea710c2decb7.png

  1. เมื่อคุณเปิดพื้นผิวการออกแบบให้คลิกขวาที่ภาพตัวอย่างแล้วเลือก แปลงเป็น MotionLayout

4fa936a98a8393b9.png

สิ่งนี้จะแทนที่แท็ก 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

66d0e80d5ab4daf8.png

มีองค์ประกอบ UI ใหม่สามอย่างใน Motion Editor:

  1. ภาพรวม - นี่คือการเลือกโมดอลที่ช่วยให้คุณสามารถเลือกส่วนต่างๆของภาพเคลื่อนไหวได้ ในภาพนี้จะมีการเลือก start ConstraintSet คุณยังสามารถเลือกการเปลี่ยนแปลงระหว่าง start และ end โดยคลิกที่ลูกศรระหว่าง start และ end
  2. ส่วน - ด้านล่างภาพรวมคือหน้าต่างส่วนที่เปลี่ยนแปลงตามรายการภาพรวมที่เลือกในปัจจุบัน ในภาพนี้ข้อมูล start ConstraintSet จะแสดงในหน้าต่างการเลือก
  3. แอตทริบิวต์ - แผงแอตทริบิวต์จะแสดงและอนุญาตให้คุณแก้ไขแอตทริบิวต์ของรายการที่เลือกในปัจจุบันจากหน้าต่างภาพรวมหรือหน้าต่างการเลือก ในภาพนี้แสดงแอตทริบิวต์สำหรับ start ConstraintSet

ขั้นตอนที่ 3: กำหนดข้อ จำกัด ในการเริ่มต้นและสิ้นสุด

ภาพเคลื่อนไหวทั้งหมดสามารถกำหนดได้ในรูปแบบของจุดเริ่มต้นและจุดสิ้นสุด จุดเริ่มต้นอธิบายลักษณะของหน้าจอก่อนภาพเคลื่อนไหวและตอนท้ายจะอธิบายลักษณะของหน้าจอหลังจากภาพเคลื่อนไหวเสร็จสิ้น MotionLayout รับผิดชอบในการค้นหาวิธีการเคลื่อนไหวระหว่างสถานะเริ่มต้นและสถานะสิ้นสุด (เมื่อเวลาผ่านไป)

MotionScene ใช้แท็ก ConstraintSet เพื่อกำหนดสถานะเริ่มต้นและสิ้นสุด ConstraintSet คือสิ่งที่ดูเหมือนชุดของข้อ จำกัด ที่สามารถนำไปใช้กับมุมมองได้ ซึ่งรวมถึงความกว้างความสูงและ ConstraintLayout นอกจากนี้ยังมีคุณลักษณะบางอย่างเช่น alpha ไม่มีมุมมองในตัวเองเป็นเพียงข้อ จำกัด ของมุมมองเหล่านั้น

ข้อ จำกัด ใด ๆ ที่ระบุใน ConstraintSet จะลบล้างข้อ จำกัด ที่ระบุในไฟล์โครงร่าง หากคุณกำหนดข้อ จำกัด ทั้งในเค้าโครงและ MotionScene จะใช้เฉพาะข้อ จำกัด ใน MotionScene เท่านั้น

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

คุณสามารถทำตามขั้นตอนนี้ได้โดยใช้ Motion Editor หรือแก้ไขข้อความของ activity_step1_scene.xml โดยตรง

  1. เลือก start ConstraintSet ในแผงภาพรวม

6e57661ed358b860.png

  1. ในแผงตัวเลือกให้เลือก red_star ขณะนี้จะแสดงแหล่งที่มาของ layout ซึ่งหมายความว่าไม่มีข้อ จำกัด ใน ConstraintSet นี้ ใช้ไอคอนดินสอที่ด้านขวาบนเพื่อ สร้างข้อ จำกัด

f9564c574b86ea8.gif

  1. ตรวจสอบให้ red_star ว่า red_star แสดงแหล่งที่มาของการ start เมื่อเลือก start ConstraintSet ในแผงภาพรวม
  2. ในแผงแอตทริบิวต์ที่มีการเลือก red_star ในการ start ConstraintSet ให้เพิ่มข้อ จำกัด ที่ด้านบนและเริ่มต้นด้วยการคลิกที่ปุ่มสีฟ้า +

2fce076cd7b04bd.png

  1. เปิด 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 เดียว

Constraint ภายใน ConstraintSet ระบุ id ของมุมมองที่ จำกัด @id/red_star กำหนดใน activity_step1.xml สิ่งสำคัญคือต้องทราบว่าแท็ก Constraint ระบุเฉพาะข้อ จำกัด และข้อมูลเค้าโครงเท่านั้น แท็ก Constraint ไม่ทราบว่ามีการนำไปใช้กับ ImageView

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

  1. เลือก end ConstraintSet ในแผงภาพรวม

346e1248639b6f1e.png

  1. ทำตามขั้นตอนเดียวกับที่คุณทำก่อนหน้านี้เพื่อเพิ่ม Constraint สำหรับ red_star ใน end ConstraintSet
  2. หากต้องการใช้ Motion Editor เพื่อทำขั้นตอนนี้ให้เสร็จสิ้นให้เพิ่มข้อ จำกัด ที่ bottom และ end โดยคลิกที่ปุ่ม + สีน้ำเงิน

fd33c779ff83c80a.png

  1. โค้ดใน 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>

เช่นเดียวกับ @id/start ConstraintSet นี้มี Constraint เดียวที่ @id/red_star คราวนี้จะ จำกัด ไว้ที่ส่วนล่างสุดของหน้าจอ

คุณไม่จำเป็นต้องตั้งชื่อว่า @id/start และ @id/end แต่สะดวกที่จะทำเช่นนั้น

ขั้นตอนที่ 4: กำหนดการเปลี่ยนแปลง

ทุก MotionScene จะต้องมีการเปลี่ยนแปลงอย่างน้อยหนึ่งครั้งด้วย การเปลี่ยนแปลงกำหนดทุกส่วนของแอนิเมชั่นหนึ่งเรื่องตั้งแต่ต้นจนจบ

การเปลี่ยนแปลงต้องระบุการเริ่มต้นและสิ้นสุด ConstraintSet สำหรับการเปลี่ยนแปลง การเปลี่ยนแปลงยังสามารถระบุวิธีแก้ไขภาพเคลื่อนไหวด้วยวิธีอื่น ๆ เช่นระยะเวลาในการเรียกใช้ภาพเคลื่อนไหวหรือวิธีการทำให้เคลื่อนไหวโดยการลากมุมมอง

  1. 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 ระบุ duration ที่ภาพเคลื่อนไหวควรใช้เป็นมิลลิวินาที

จากนั้น MotionLayout จะหาเส้นทางระหว่างข้อ จำกัด เริ่มต้นและจุดสิ้นสุดและทำให้เคลื่อนไหวได้ตามระยะเวลาที่กำหนด

ขั้นตอนที่ 5: ดูตัวอย่างภาพเคลื่อนไหวใน Motion Editor

dff9ecdc1f4a0740.gif

ภาพเคลื่อนไหว: วิดีโอแสดงตัวอย่างการเปลี่ยนแปลงใน Motion Editor

  1. เปิด Motion Editor และเลือกการเปลี่ยนแปลงโดยคลิกที่ลูกศรระหว่าง start และ end ในแผงภาพรวม

1dc541ae8c43b250.png

  1. แผงการ เลือก จะแสดงการควบคุมการเล่นและแถบสครับเมื่อเลือกการเปลี่ยน คลิกเล่นหรือลากตำแหน่งปัจจุบันเพื่อดูตัวอย่างภาพเคลื่อนไหว

a0fd2593384dfb36.png

ขั้นตอนที่ 6: เพิ่มตัวจัดการคลิก

คุณต้องการวิธีเริ่มแอนิเมชั่น วิธีหนึ่งที่ทำได้คือทำให้ MotionLayout ตอบสนองต่อเหตุการณ์การคลิกบน @id/red_star

  1. เปิดตัวแก้ไขการเคลื่อนไหวและเลือกการเปลี่ยนแปลงโดยคลิกที่ลูกศรระหว่างจุดเริ่มต้นและจุดสิ้นสุดในแผงภาพรวม

b6f94b344ce65290.png

  1. คลิก 699f7ae04024ccf6.png สร้างตัวจัดการการคลิกหรือการปัด ในแถบเครื่องมือสำหรับแผงภาพรวม สิ่งนี้จะเพิ่มตัวจัดการที่จะเริ่มการเปลี่ยนแปลง
  2. เลือก Click Handler จากป๊อปอัป

ccf92d06335105fe.png

  1. เปลี่ยน View To Click เป็น red_star

b0d3f0c970604f01.png

  1. คลิก เพิ่ม ตัวจัดการการคลิกจะแสดงด้วยจุดเล็ก ๆ บนตัวแก้ไขการเปลี่ยนแปลงในการเคลื่อนไหว

cec3913e67fb4105.png

  1. เมื่อเลือกการเปลี่ยนแปลงในแผงภาพรวมให้เพิ่มแอตทริบิวต์ clickAction ของการ toggle ไปยังตัวจัดการ OnClick ที่คุณเพิ่งเพิ่มในแผงแอตทริบิวต์

9af6fc60673d093d.png

  1. เปิด 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 คือข้อมูลพ targetId สำหรับการคลิก
  • clickAction ของการ toggle จะสลับระหว่างสถานะเริ่มต้นและสถานะสิ้นสุดเมื่อคลิก คุณสามารถดูตัวเลือกอื่น ๆ สำหรับ clickAction ใน เอกสารประกอบ
  1. เรียกใช้รหัสของคุณคลิก ขั้นตอนที่ 1 จากนั้นคลิกดาวสีแดงและดูภาพเคลื่อนไหว!

ขั้นตอนที่ 5: แอนิเมชั่นในการดำเนินการ

เรียกใช้แอพ! คุณควรเห็นภาพเคลื่อนไหวของคุณทำงานเมื่อคุณคลิกที่ดาว

7ba88af963fdfe10.gif

ไฟล์ฉากการเคลื่อนไหวที่เสร็จสมบูรณ์กำหนดหนึ่งการ Transition ซึ่งชี้ไปที่จุดเริ่มต้นและจุดสิ้นสุด 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>

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

ขั้นตอนที่ 1: ตรวจสอบรหัสเริ่มต้น

  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>

เค้าโครงนี้กำหนดมุมมองทั้งหมดสำหรับภาพเคลื่อนไหว ไอคอนรูปดาวสามดวงไม่มีข้อ จำกัด ในการจัดวางเนื่องจากจะมีการเคลื่อนไหวในฉากเคลื่อนไหว

เครดิต TextView มีข้อ จำกัด ที่นำไปใช้เนื่องจากยังคงอยู่ที่เดิมสำหรับภาพเคลื่อนไหวทั้งหมดและไม่ได้แก้ไขแอตทริบิวต์ใด ๆ

ขั้นตอนที่ 2: ทำให้ฉากเคลื่อนไหว

เช่นเดียวกับภาพเคลื่อนไหวสุดท้ายภาพเคลื่อนไหวจะถูกกำหนดโดยการเริ่มต้นและสิ้นสุด ConstraintSet, และการ Transition

กำหนดค่าเริ่มต้น ConstraintSet

  1. เปิดฉากการเคลื่อนไหว xml/step2.xml เพื่อกำหนดภาพเคลื่อนไหว
  2. เพิ่มข้อ จำกัด สำหรับการเริ่มต้นข้อ จำกัด start เมื่อเริ่มต้นดาวทั้งสามดวงจะอยู่ตรงกลางที่ด้านล่างของหน้าจอ ดาวด้านขวาและด้านซ้ายมีค่า 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 สำหรับแต่ละดาว ข้อ จำกัด แต่ละข้อจะถูกนำไปใช้โดย MotionLayout ในช่วงเริ่มต้นของภาพเคลื่อนไหว

มุมมองดาวแต่ละดวงจะอยู่กึ่งกลางที่ด้านล่างของหน้าจอโดยใช้ข้อ จำกัด เริ่มต้นสิ้นสุดและด้านล่าง ทั้งสองดาว @id/left_star และ @id/right_star ทั้งสองมีค่าอัลฟาเพิ่มเติมที่ทำให้มองไม่เห็นและจะถูกนำไปใช้เมื่อเริ่มภาพเคลื่อนไหว

ชุดข้อ จำกัด start และ end กำหนดจุดเริ่มต้นและจุดสิ้นสุดของภาพเคลื่อนไหว ข้อ จำกัด ในการเริ่มต้นเช่น motion:layout_constraintStart_toStartOf จะ จำกัด การเริ่มต้นของมุมมองไปยังจุดเริ่มต้นของมุมมองอื่น สิ่งนี้อาจทำให้สับสนในตอนแรกเนื่องจากมีการใช้ชื่อ start สำหรับทั้งคู่ และ ทั้งคู่ใช้ในบริบทของข้อ จำกัด เพื่อช่วยดึงความแตกต่างออกไปการ start ใน layout_constraintStart หมายถึง "เริ่มต้น" ของมุมมองซึ่งเป็นภาษาซ้ายไปขวาและขวาในภาษาจากขวาไปซ้าย ชุดข้อ จำกัด ในการ start หมายถึงจุดเริ่มต้นของภาพเคลื่อนไหว

กำหนดจุดสิ้นสุด ConstraintSet

  1. กำหนดข้อ จำกัด ในการใช้ โซ่ เพื่อวางตำแหน่งดาวทั้งสามไว้ด้วยกันด้านล่าง @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 ทั้งสองมุมมองจะจางหายไปเมื่อภาพเคลื่อนไหวดำเนินไป

เคลื่อนไหวตามการปัดของผู้ใช้

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

  1. แทนที่สิ่งที่ต้องทำเพื่อเพิ่มแท็ก 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 มีแอตทริบิวต์บางอย่างที่สำคัญที่สุดคือ touchAnchorId

  • touchAnchorId คือมุมมองที่ถูกติดตามซึ่งเคลื่อนที่เพื่อตอบสนองต่อการสัมผัส MotionLayout จะทำให้มุมมองนี้อยู่ในระยะเท่าเดิมจากนิ้วที่กำลังปัด
  • touchAnchorSide กำหนดว่าควรติดตามด้านใดของมุมมอง สิ่งนี้สำคัญสำหรับมุมมองที่ปรับขนาดตามเส้นทางที่ซับซ้อนหรือมีด้านหนึ่งที่เคลื่อนที่เร็วกว่าอีกด้านหนึ่ง
  • dragDirection กำหนดทิศทางที่สำคัญสำหรับภาพเคลื่อนไหวนี้ (ขึ้นลงซ้ายหรือขวา)

เมื่อ MotionLayout รับฟังเหตุการณ์การลากผู้ฟังจะถูกลงทะเบียนในมุมมอง MotionLayout ไม่ใช่มุมมองที่ระบุโดย touchAnchorId เมื่อผู้ใช้เริ่มท่าทางสัมผัสที่ใดก็ได้บนหน้าจอ MotionLayout จะรักษาระยะห่างระหว่างนิ้วของพวกเขากับการ touchAnchorSide ของค่าคงที่มุมมอง touchAnchorId ตัวอย่างเช่นหากพวกเขาแตะ 100dp จากด้านจุดยึดเช่น MotionLayout จะทำให้ด้านนั้นห่างจากนิ้ว 100dp สำหรับภาพเคลื่อนไหวทั้งหมด

ลองใช้งาน

  1. เรียกใช้แอพอีกครั้งและเปิดหน้าจอขั้นตอนที่ 2 คุณจะเห็นภาพเคลื่อนไหว
  2. ลอง "เหวี่ยง" หรือปล่อยนิ้วของคุณไปครึ่งทางของแอนิเมชั่นเพื่อสำรวจว่า MotionLayout แสดงภาพเคลื่อนไหวตามฟิสิกส์ของของไหลอย่างไร!

fefcdd690a0dcaec.gif

MotionLayout สามารถเคลื่อนไหวระหว่างการออกแบบที่แตกต่างกันโดยใช้คุณสมบัติจาก ConstraintLayout เพื่อสร้างเอฟเฟกต์ที่หลากหลาย

ในภาพเคลื่อนไหวนี้มุมมองทั้งสามจะอยู่ในตำแหน่งที่สัมพันธ์กับระดับบนสุดของหน้าจอเพื่อเริ่มต้น ในตอนท้ายมุมมองทั้งสามจะอยู่ในตำแหน่งที่สัมพันธ์กับ @id/credits ในห่วงโซ่

แม้จะมีเค้าโครงที่แตกต่างกันมากเหล่านี้ MotionLayout จะสร้างภาพเคลื่อนไหวที่ลื่นไหลระหว่างจุดเริ่มต้นและจุดสิ้นสุด

ในขั้นตอนนี้คุณจะต้องสร้างแอนิเมชั่นที่เดินตามเส้นทางที่ซับซ้อนระหว่างการเคลื่อนไหวและทำให้เครดิตเคลื่อนไหวในระหว่างการเคลื่อนไหว MotionLayout สามารถแก้ไขเส้นทางที่มุมมองจะใช้ระหว่างจุดเริ่มต้นและจุดสิ้นสุดโดยใช้ KeyPosition

ขั้นตอนที่ 1: สำรวจโค้ดที่มีอยู่

  1. เปิด layout/activity_step3.xml และ xml/step3.xml เพื่อดูเค้าโครงและฉากการเคลื่อนไหวที่มีอยู่ ImageView และ TextView แสดงดวงจันทร์และข้อความเครดิต
  2. เปิดไฟล์ฉากการเคลื่อนไหว ( xml/step3.xml ) คุณจะเห็นว่ามีการกำหนดการ Transition จาก @id/start เป็น @id/end ภาพเคลื่อนไหวจะย้ายภาพดวงจันทร์จากด้านซ้ายล่างของหน้าจอไปที่ด้านล่างขวาของหน้าจอโดยใช้ ConstraintSets สองชุด ข้อความเครดิตจะจางหายไปจาก alpha="0.0" ถึง alpha="1.0" ขณะที่ดวงจันทร์กำลังเคลื่อนที่
  3. เรียกใช้แอปทันทีและเลือก ขั้นตอนที่ 3 คุณจะเห็นว่าดวงจันทร์เดินตามเส้นตรง (หรือเส้นตรง) ตั้งแต่ต้นจนจบเมื่อคุณคลิกบนดวงจันทร์

ขั้นตอนที่ 2: เปิดใช้งานการดีบักเส้นทาง

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

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

  1. ในการเปิดใช้งานเส้นทางการดีบักให้เปิด 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" >

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

23bbb604f456f65c.png

  • วงกลม แสดงตำแหน่งเริ่มต้นหรือสิ้นสุดของมุมมองเดียว
  • เส้น แสดงเส้นทางของมุมมองเดียว
  • เพชร เป็นตัวแทนของ KeyPosition ที่ปรับเปลี่ยนเส้นทาง

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

ขั้นตอนที่ 3: แก้ไขเส้นทาง

ภาพเคลื่อนไหวทั้งหมดใน MotionLayout ถูกกำหนดโดยจุดเริ่มต้นและจุดสิ้นสุด ConstraintSet ที่กำหนดลักษณะของหน้าจอก่อนที่ภาพเคลื่อนไหวจะเริ่มและหลังจากภาพเคลื่อนไหวเสร็จสิ้น ตามค่าเริ่มต้น MotionLayout วางแผนเส้นทางเชิงเส้น (เส้นตรง) ระหว่างตำแหน่งเริ่มต้นและตำแหน่งสิ้นสุดของแต่ละมุมมองที่เปลี่ยนตำแหน่ง

ในการสร้างเส้นทางที่ซับซ้อนเช่นส่วนโค้งของดวงจันทร์ในตัวอย่างนี้ MotionLayout ใช้ KeyPosition เพื่อแก้ไขเส้นทางที่มุมมองใช้ระหว่างจุดเริ่มต้นและจุดสิ้นสุด

  1. เปิด xml/step3.xml และเพิ่ม KeyPosition ให้กับฉาก แท็ก KeyPosition จะอยู่ภายในแท็กการ Transition

eae4dae9a12d0410.png

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 เพื่อปรับแต่งได้

ลองใช้งาน

หากคุณเรียกใช้แอปอีกครั้งคุณจะเห็นภาพเคลื่อนไหวสำหรับขั้นตอนนี้

46b179c01801f19e.gif

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

หากคุณมองอย่างใกล้ชิดข้อความเครดิตจะถูก จำกัด โดยตำแหน่งของดวงจันทร์ ทำไมมันไม่เคลื่อนที่ในแนวตั้งด้วย?

1c7cf779931e45cc.gif

<Constraint
       android:id="@id/credits"
       ...
       motion:layout_constraintBottom_toBottomOf="@id/moon"
       motion:layout_constraintTop_toTopOf="@id/moon"
/>

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

หากคุณไม่ต้องการให้เครดิตเคลื่อนที่ไปพร้อมกับดวงจันทร์คุณสามารถเพิ่ม KeyPosition ให้กับเครดิตหรือแก้ไขข้อ จำกัด ในการเริ่มต้นที่ @id/credits KeyPosition

ในส่วนถัดไปคุณจะเข้าสู่ประเภทต่างๆของ keyPositionType ใน MotionLayout

ในขั้นตอนสุดท้ายที่คุณใช้ 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 เป็นไปได้: parentRelative , pathRelative และ deltaRelative การระบุประเภทจะเปลี่ยนระบบพิกัดที่ percentX และ percentY

ระบบพิกัดคืออะไร?

ระบบพิกัด ให้วิธีระบุจุดในอวกาศ นอกจากนี้ยังมีประโยชน์สำหรับการอธิบายตำแหน่งบนหน้าจอ

ระบบพิกัด MotionLayout เป็น ระบบพิกัดคาร์ทีเซียน ซึ่งหมายความว่ามีแกน X และแกน Y กำหนดโดยเส้นตั้งฉากสองเส้น ความแตกต่างที่สำคัญระหว่างพวกเขาคือตำแหน่งที่แกน X ไปบนหน้าจอ (แกน Y จะตั้งฉากกับแกน X เสมอ)

ระบบพิกัดทั้งหมดใน MotionLayout ใช้ค่าระหว่าง 0.0 ถึง 1.0 ทั้งบนแกน X และ Y อนุญาตให้มีค่าเชิงลบและค่าที่มากกว่า 1.0 ตัวอย่างเช่นค่า percentX เท่ากับ -2.0 จะหมายถึงไปในทิศทางตรงกันข้ามกับแกน X สองครั้ง

หากทั้งหมดนั้นฟังดูคล้ายกับคลาสพีชคณิตมากเกินไปลองดูภาพด้านล่างนี้!

พิกัด parentRelative

a7b7568d46d9dec7.png

keyPositionType ของ parentRelative ใช้ระบบพิกัดเดียวกันกับหน้าจอ กำหนด (0, 0) ทางด้านซ้ายบนของ MotionLayout ทั้งหมดและ (1, 1) ไปทางขวาล่าง

คุณสามารถใช้ parentRelative ทุกเมื่อที่คุณต้องการสร้างแอนิเมชั่นที่เคลื่อนที่ผ่าน MotionLayout ทั้งหมดเช่นส่วนโค้งของดวงจันทร์ในตัวอย่างนี้

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

พิกัดเดลต้า

5680bf553627416c.png

Delta เป็นศัพท์ทางคณิตศาสตร์สำหรับการเปลี่ยนแปลงดังนั้น deltaRelative จึงเป็นวิธีการพูดว่า "เปลี่ยนญาติ" ในพิกัด deltaRelative (0,0) คือตำแหน่งเริ่มต้นของมุมมองและ (1,1) คือตำแหน่งสิ้นสุด แกน X และ Y อยู่ในแนวเดียวกันกับหน้าจอ

แกน X เป็นแนวนอนบนหน้าจอเสมอและแกน Y จะอยู่ในแนวตั้งบนหน้าจอเสมอ เมื่อเทียบกับ parentRelative ความแตกต่างที่สำคัญคือพิกัดจะอธิบายเฉพาะส่วนของหน้าจอที่มุมมองจะเคลื่อนที่

deltaRelative เป็นระบบพิกัดที่ยอดเยี่ยมสำหรับการควบคุมการเคลื่อนที่ในแนวนอนหรือแนวตั้งโดยแยกออกจากกัน ตัวอย่างเช่นคุณสามารถสร้างแอนิเมชั่นที่ทำให้การเคลื่อนไหวในแนวตั้ง (Y) เสร็จสมบูรณ์ที่ 50% และยังคงเคลื่อนไหวในแนวนอน (X)

พิกัดสัมพัทธ์

f3aaadaac8b4a93f.png

ระบบพิกัดสุดท้ายใน MotionLayout คือ pathRelative มันค่อนข้างแตกต่างจากอีกสองอันเนื่องจากแกน X เป็นไปตามเส้นทางการเคลื่อนที่ตั้งแต่ต้นจนจบ ดังนั้น (0,0) คือตำแหน่งเริ่มต้นและ (1,0) คือตำแหน่งสิ้นสุด

ทำไมคุณถึงต้องการสิ่งนี้? มันค่อนข้างน่าแปลกใจเมื่อมองแวบแรกโดยเฉพาะอย่างยิ่งเนื่องจากระบบพิกัดนี้ไม่ได้อยู่ในแนวเดียวกับระบบพิกัดหน้าจอ

ปรากฎว่า pathRelative มีประโยชน์สำหรับบางสิ่ง

  • การเร่งความเร็วการชะลอตัวหรือการหยุดการดูระหว่างส่วนหนึ่งของภาพเคลื่อนไหว เนื่องจากมิติ X มักจะตรงกับเส้นทางที่มุมมองใช้ทุก pathRelative คุณจึงสามารถใช้ pathRelative KeyPosition เพื่อเปลี่ยน framePosition ที่จุดใดจุดหนึ่งในเส้นทางนั้นถึง ดังนั้น KeyPosition ที่ framePosition="50" มี percentX="0.1" จะทำให้แอนิเมชั่นใช้เวลา 50% ในการเดินทาง 10% แรกของการเคลื่อนไหว
  • การเพิ่มส่วนโค้งที่ละเอียดอ่อนให้กับเส้นทาง เนื่องจากมิติ Y ตั้งฉากกับการเคลื่อนที่เสมอการเปลี่ยน Y จะเปลี่ยนเส้นทางเป็นเส้นโค้งโดยสัมพันธ์กับการเคลื่อนที่โดยรวม
  • การเพิ่มมิติที่สองเมื่อ deltaRelative ไม่ทำงาน สำหรับการเคลื่อนที่ในแนวนอนและแนวตั้งอย่างสมบูรณ์ deltaRelative จะสร้างมิติข้อมูลที่มีประโยชน์เพียงมิติเดียว อย่างไรก็ตาม pathRelative จะสร้างพิกัด X และ Y ที่ใช้งานได้เสมอ

ในขั้นตอนต่อไปคุณจะได้เรียนรู้วิธีสร้างเส้นทางที่ซับซ้อนยิ่งขึ้นโดยใช้ KeyPosition มากกว่าหนึ่ง KeyPosition

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

แก้ไขเส้นทางด้วยองค์ประกอบ KeyPosition หลายรายการ

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

  1. เปิด xml/step4.xml คุณจะเห็นว่ามีมุมมองเดียวกันและ KeyFrame คุณเพิ่มในขั้นตอนสุดท้าย
  2. หากต้องการปัดเศษด้านบนสุดของเส้นโค้งให้เพิ่ม KeyPositions อีกสอง KeyPositions ไปยังเส้นทางของ @id/moon อันหนึ่งก่อนถึงจุดสูงสุดและอีกหนึ่งตำแหน่งตามหลัง

500b5ac2db48ef87.png

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

ลองใช้งาน

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

สำรวจด้วยตัวคุณเอง

ก่อนที่คุณจะไปยัง KeyFrame ประเภทอื่นให้ลองเพิ่ม KeyPositions เพิ่มเติมใน KeyFrameSet เพื่อดูว่าคุณสามารถสร้างเอฟเฟกต์ประเภทใดได้โดยใช้ KeyPosition

นี่คือตัวอย่างหนึ่งที่แสดงวิธีสร้างเส้นทางที่ซับซ้อนซึ่งเคลื่อนที่ไปมาระหว่างการเคลื่อนไหว

cd9faaffde3dfef.png

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 ประเภทอื่น ๆ

การสร้างภาพเคลื่อนไหวแบบไดนามิกมักหมายถึงการเปลี่ยน size rotation หรือ alpha ของมุมมองเมื่อภาพเคลื่อนไหวดำเนินไป MotionLayout สนับสนุนการทำให้ แอตทริบิวต์ จำนวนมากเคลื่อนไหวในทุกมุมมองโดยใช้ KeyAttribute

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

ขั้นตอนที่ 1: ปรับขนาดและหมุนด้วย KeyAttribute

  1. เปิด xml/step5.xml ซึ่งมีภาพเคลื่อนไหวเดียวกับที่คุณสร้างในขั้นตอนสุดท้าย เพื่อความหลากหลายหน้าจอนี้จะใช้ภาพอวกาศที่แตกต่างกันเป็นพื้นหลัง
  2. ในการทำให้ดวงจันทร์ขยายขนาดและหมุนให้เพิ่มแท็ก KeyAttribute สองแท็กใน KeyFrameSet ที่ keyFrame="50" และ keyFrame="100"

bbae524a2898569.png

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% จะเกิดขึ้นที่ด้านบนสุดของส่วนโค้งและทำให้มุมมองมีขนาดเพิ่มขึ้นเป็นสองเท่าและหมุน -360 องศา (หรือหนึ่งวงกลมเต็ม) KeyAttribute ที่สองจะสิ้นสุดการหมุนครั้งที่สองเป็น -720 องศา (วงกลมเต็มสองวง) และย่อขนาดกลับเป็นปกติเนื่องจากค่า scaleX และ scaleY เริ่มต้นเป็น 1.0

เช่นเดียวกับ KeyPosition KeyAttribute ใช้ framePosition และ motionTarget เพื่อระบุเวลาที่จะใช้ KeyFrame และมุมมองที่จะแก้ไข MotionLayout จะสอดแทรกระหว่าง KeyPositions เพื่อสร้างภาพเคลื่อนไหวที่ลื่นไหล

KeyAttributes สนับสนุน แอตทริบิวต์ ที่สามารถนำไปใช้กับมุมมองทั้งหมด สนับสนุนการเปลี่ยนแปลงคุณลักษณะพื้นฐานเช่นการ visibility alpha หรือ elevation นอกจากนี้คุณยังสามารถเปลี่ยนการหมุนได้เช่นเดียวกับที่คุณทำอยู่ที่นี่หมุนเป็นสามมิติด้วยการ rotateX และ rotateY ปรับขนาดด้วย scaleX และ scaleY หรือแปลตำแหน่งของมุมมองเป็น X, Y หรือ Z

ขั้นตอนที่ 2: ชะลอการปรากฏของเครดิต

เป้าหมายอย่างหนึ่งของขั้นตอนนี้คือการอัปเดตภาพเคลื่อนไหวเพื่อไม่ให้ข้อความเครดิตปรากฏจนกว่าภาพเคลื่อนไหวส่วนใหญ่จะเสร็จสมบูรณ์

  1. จะชะลอการปรากฏตัวของสินเชื่อที่กำหนดอีกหนึ่ง KeyAttribute ที่ทำให้มั่นใจว่า 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 KeyAttribute ไว้ที่ 0.0 สำหรับ 85% แรกของแอนิเมชั่น เนื่องจากเริ่มต้นที่อัลฟาเป็น 0 จึงทำให้มองไม่เห็น 85% แรกของภาพเคลื่อนไหว

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

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

ลองใช้งาน

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

2f4bfdd681c1fa98.gif

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

สำรวจด้วยตัวคุณเอง

ก่อนที่คุณจะไปยัง 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

ภาพเคลื่อนไหวที่สมบูรณ์เกี่ยวข้องกับการเปลี่ยนสีหรือคุณลักษณะอื่น ๆ ของมุมมอง ในขณะที่ MotionLayout สามารถใช้ KeyAttribute เพื่อเปลี่ยนแอตทริบิวต์มาตรฐานใด ๆ ที่แสดงรายการในงานก่อนหน้านี้คุณใช้ CustomAttribute เพื่อระบุแอตทริบิวต์อื่น ๆ

CustomAttribute สามารถใช้เพื่อตั้งค่าใด ๆ ที่มีตัวตั้งค่า ตัวอย่างเช่นคุณสามารถตั้งค่า backgroundColor บนมุมมองโดยใช้ CustomAttribute MotionLayout จะใช้การ สะท้อน เพื่อค้นหาตัวตั้งค่าจากนั้นเรียกมันซ้ำ ๆ เพื่อทำให้มุมมองเคลื่อนไหว

ในขั้นตอนนี้คุณจะใช้ CustomAttribute เพื่อตั้งค่าแอตทริบิวต์ colorFilter บนดวงจันทร์เพื่อสร้างภาพเคลื่อนไหวที่แสดงด้านล่าง

5fb6792126a09fda.gif

กำหนดแอตทริบิวต์ที่กำหนดเอง

  1. ในการเริ่มต้นเปิด xml/step6.xml ซึ่งมีภาพเคลื่อนไหวเดียวกับที่คุณสร้างในขั้นตอนสุดท้าย
  2. ในการทำให้ดวงจันทร์เปลี่ยนสีให้เพิ่ม KeyAttribute สอง KeyAttribute พร้อม CustomAttribute ใน KeyFrameSet ที่ keyFrame="0" , keyFrame="50" และ keyFrame="100".

214699d5fdd956da.png

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 และหนึ่งค่าที่จะตั้งค่า

  • motion:attributeName คือชื่อของ setter ที่จะถูกเรียกโดยแอตทริบิวต์ที่กำหนดเองนี้ ในตัวอย่างนี้ setColorFilter บน Drawable จะถูกเรียก
  • motion:custom*Value คือค่าที่กำหนดเองของประเภทที่ระบุไว้ในชื่อในตัวอย่างนี้ค่าที่กำหนดเองคือสีที่ระบุ

ค่าที่กำหนดเองสามารถมีประเภทใดก็ได้ดังต่อไปนี้:

  • สี
  • จำนวนเต็ม
  • ลอย
  • สตริง
  • มิติ
  • บูลีน

การใช้ API นี้ MotionLayout สามารถเคลื่อนไหวทุกอย่างที่ให้ตัวตั้งค่าในมุมมองใดก็ได้

ลองใช้งาน

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

5fb6792126a09fda.gif

เมื่อคุณเพิ่ม KeyFrames เพิ่มเติม MotionLayout จะเปลี่ยนเส้นทางของดวงจันทร์จากเส้นตรงไปเป็นเส้นโค้งที่ซับซ้อนเพิ่มการตีกลับสองครั้งการปรับขนาดและการเปลี่ยนสีระหว่างภาพเคลื่อนไหว

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

ในขั้นตอนนี้คุณจะสำรวจโดยใช้ OnSwipe กับเส้นทางที่ซับซ้อน จนถึงขณะนี้แอนิเมชั่นของดวงจันทร์ถูกเรียกใช้โดยผู้ฟัง OnClick และทำงานตามระยะเวลาที่กำหนด

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

ขั้นตอนที่ 1: สำรวจพฤติกรรม OnSwipe

  1. เปิด xml/step7.xml และค้นหาการประกาศ OnSwipe ที่มีอยู่

step7.xml

<!-- Fix OnSwipe by changing touchAnchorSide →

<OnSwipe
       motion:touchAnchorId="@id/moon"
       motion:touchAnchorSide="bottom"
/>
  1. เรียกใช้แอปบนอุปกรณ์ของคุณและไปที่ ขั้นตอนที่ 7 ดูว่าคุณสามารถสร้างภาพเคลื่อนไหวที่ราบรื่นได้หรือไม่โดยการลากดวงจันทร์ไปตามเส้นทางของส่วนโค้ง

เมื่อคุณเรียกใช้แอนิเมชั่นนี้มันดูไม่ค่อยดีนัก หลังจากดวงจันทร์ขึ้นถึงจุดสูงสุดของส่วนโค้งแล้วมันจะเริ่มกระโดดไปรอบ ๆ

ed96e3674854a548.gif

หากต้องการทำความเข้าใจข้อบกพร่องให้พิจารณาว่าจะเกิดอะไรขึ้นเมื่อผู้ใช้แตะที่ด้านล่างส่วนบนสุดของส่วนโค้ง เนื่องจากแท็ก OnSwipe มีการ motion:touchAnchorSide="bottom" MotionLayout จะพยายามทำให้ระยะห่างระหว่างนิ้วและด้านล่างของมุมมองคงที่ตลอดทั้งภาพเคลื่อนไหว

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

56cd575c5c77eddd.png

ขั้นตอนที่ 2: ใช้ด้านขวา

เพื่อหลีกเลี่ยงข้อบกพร่องเช่นนี้สิ่งสำคัญคือต้องเลือก touchAnchorId และ touchAnchorSide ที่ดำเนินไปในทิศทางเดียวตลอดระยะเวลาของภาพเคลื่อนไหวทั้งหมด

ในแอนิเมชั่นนี้ทั้งด้าน right และ left ของดวงจันทร์จะเคลื่อนผ่านหน้าจอไปในทิศทางเดียว

อย่างไรก็ตามทั้ง bottom และ top จะกลับทิศทาง เมื่อ OnSwipe พยายามติดตามพวกเขาจะสับสนเมื่อทิศทางของพวกเขาเปลี่ยนไป

  1. ในการทำให้แอนิเมชั่นนี้เป็นไปตามเหตุการณ์การสัมผัสให้เปลี่ยน 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

  1. อัปเดต OnSwipe เพื่อติดตามการเคลื่อนไหวของดวงจันทร์อย่างถูกต้อง

step7.xml

<!-- Using dragDirection to control the direction of drag tracking →

<OnSwipe
       motion:touchAnchorId="@id/moon"
       motion:touchAnchorSide="bottom"
       motion:dragDirection="dragRight"
/>

ลองใช้งาน

  1. เรียกใช้แอปอีกครั้งแล้วลองลากดวงจันทร์ไปตามเส้นทางทั้งหมด แม้ว่าจะเป็นไปตามส่วนโค้งที่ซับซ้อน แต่ MotionLayout จะสามารถดำเนินการต่อภาพเคลื่อนไหวเพื่อตอบสนองต่อเหตุการณ์การปัดนิ้ว

5458dff382261427.gif

MotionLayout สามารถใช้เพื่อสร้างภาพเคลื่อนไหวที่สมบูรณ์เมื่อใช้กับ CoordinatorLayout ในขั้นตอนนี้คุณจะต้องสร้างส่วนหัวที่ยุบได้โดยใช้ MotionLayout

ขั้นตอนที่ 1: สำรวจโค้ดที่มีอยู่

  1. ในการเริ่มต้นให้เปิด layout/activity_step8.xml
  2. ใน 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 ได้

  1. เรียกใช้แอพและไปที่ ขั้นตอนที่ 8 คุณจะเห็นว่าเมื่อคุณเลื่อนข้อความดวงจันทร์จะไม่เคลื่อนที่

ขั้นตอนที่ 2: เลื่อน MotionLayout

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

ขั้นตอนที่ 3: ย้ายการเคลื่อนไหวด้วยรหัส

  1. เปิด Step8Activity.kt แก้ไขฟังก์ชัน coordinateMotion() ชั่น coordinateMotion() เพื่อบอก MotionLayout เกี่ยวกับการเปลี่ยนแปลงในตำแหน่งการเลื่อน

ขั้นที่ 8Activity.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 และเปอร์เซ็นต์ความคืบหน้าให้หารด้วยช่วงการเลื่อนทั้งหมด

ลองใช้งาน

  1. ปรับใช้แอพอีกครั้งและเรียกใช้แอนิเมชั่น ขั้นตอนที่ 8 คุณจะเห็นว่า MotionLayout จะดำเนินการเคลื่อนไหวตามตำแหน่งการเลื่อน

ee5ce4d9e33a59ca.gif

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

codelab นี้ครอบคลุม API พื้นฐานของ MotionLayout

หากต้องการดูตัวอย่างเพิ่มเติมของ MotionLayout ในทางปฏิบัติโปรดดู ตัวอย่าง อย่างเป็นทางการ และอย่าลืมตรวจสอบ เอกสารประกอบ !

เรียนรู้เพิ่มเติม

MotionLayout รองรับคุณสมบัติอื่น ๆ ที่ไม่ครอบคลุมใน codelab นี้เช่น KeyCycle, ซึ่งช่วยให้คุณควบคุมเส้นทางหรือแอตทริบิวต์ด้วยรอบการทำซ้ำและ KeyTimeCycle, ซึ่งช่วยให้คุณเคลื่อนไหวได้ตามเวลานาฬิกา ตรวจสอบตัวอย่างสำหรับตัวอย่างของแต่ละตัวอย่าง

สำหรับลิงก์ไปยัง codelabs อื่น ๆ ในหลักสูตรนี้โปรดดูที่ หน้า Landing Page ของโค้ดแล็บขั้นสูงของ Android ใน Kotlin