จัดการการเคลื่อนไหวและภาพเคลื่อนไหวของวิดเจ็ตด้วย MotionLayout

ลองใช้วิธีแบบ Compose
Jetpack Compose เป็นชุดเครื่องมือ UI ที่แนะนำสำหรับ Android ดูวิธีใช้ภาพเคลื่อนไหวใน Compose

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

MotionLayout ช่วยลดช่องว่างระหว่างการเปลี่ยนเลย์เอาต์และการจัดการการเคลื่อนไหวที่ซับซ้อน โดยมีฟีเจอร์ต่างๆ ผสมผสานกันระหว่างเฟรมเวิร์กภาพเคลื่อนไหวของพร็อพเพอร์ตี้, TransitionManager และ CoordinatorLayout

รูปที่ 1 การเคลื่อนไหวที่ควบคุมด้วยการสัมผัสขั้นพื้นฐาน

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

MotionLayout เป็นแบบประกาศทั้งหมด ซึ่งหมายความว่าคุณสามารถอธิบายการเปลี่ยนฉากใดก็ได้ใน XML ไม่ว่าจะซับซ้อนเพียงใดก็ตาม

ข้อควรพิจารณาในการออกแบบ

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

เริ่มต้นใช้งาน

ทำตามขั้นตอนต่อไปนี้เพื่อเริ่มใช้ MotionLayout ในโปรเจ็กต์

  1. เพิ่มทรัพยากร Dependency ConstraintLayout: หากต้องการใช้ MotionLayout ในโปรเจ็กต์ ให้เพิ่ม ทรัพยากร Dependency ConstraintLayout 2.0 ลงในไฟล์ build.gradle ของแอป หากคุณใช้ AndroidX ให้เพิ่มทรัพยากร Dependency ต่อไปนี้

    ดึงดูด

    dependencies {
        implementation "androidx.constraintlayout:constraintlayout:2.2.1"
        // To use constraintlayout in compose
        implementation "androidx.constraintlayout:constraintlayout-compose:1.1.1"
    }

    Kotlin

    dependencies {
        implementation("androidx.constraintlayout:constraintlayout:2.2.1")
        // To use constraintlayout in compose
        implementation("androidx.constraintlayout:constraintlayout-compose:1.1.1")
    }
  2. สร้างไฟล์ MotionLayout: MotionLayout เป็นคลาสย่อยของ ConstraintLayout คุณจึงแปลง ConstraintLayout ที่มีอยู่ เป็น MotionLayout ได้โดย แทนที่ชื่อคลาสในไฟล์ทรัพยากรเลย์เอาต์ ดังที่แสดงใน ตัวอย่างต่อไปนี้

    AndroidX

    <!-- before: ConstraintLayout -->
    <androidx.constraintlayout.widget.ConstraintLayout .../>
    <!-- after: MotionLayout -->
    <androidx.constraintlayout.motion.widget.MotionLayout .../>
              

    ไลบรารีการสนับสนุน

    <!-- before: ConstraintLayout -->
    <android.support.constraint.ConstraintLayout .../>
    <!-- after: MotionLayout -->
    <android.support.constraint.motion.MotionLayout .../>
              

    ต่อไปนี้คือตัวอย่างแบบเต็มของไฟล์ MotionLayout ซึ่ง กำหนดเลย์เอาต์ที่แสดงในรูปที่ 1

    AndroidX

    <?xml version="1.0" encoding="utf-8"?>
    <!-- activity_main.xml -->
    <androidx.constraintlayout.motion.widget.MotionLayout
        xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:app="http://schemas.android.com/apk/res-auto"
        xmlns:tools="http://schemas.android.com/tools"
        android:id="@+id/motionLayout"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        app:layoutDescription="@xml/scene_01"
        tools:showPaths="true">
    
        <View
            android:id="@+id/button"
            android:layout_width="64dp"
            android:layout_height="64dp"
            android:background="@color/colorAccent"
            android:text="Button" />
    
    </androidx.constraintlayout.motion.widget.MotionLayout>
            

    ไลบรารีการสนับสนุน

    <?xml version="1.0" encoding="utf-8"?>
    <!-- activity_main.xml -->
    <android.support.constraint.motion.MotionLayout
        xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:app="http://schemas.android.com/apk/res-auto"
        xmlns:tools="http://schemas.android.com/tools"
        android:id="@+id/motionLayout"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        app:layoutDescription="@xml/scene_01"
        tools:showPaths="true">
    
        <View
            android:id="@+id/button"
            android:layout_width="64dp"
            android:layout_height="64dp"
            android:background="@color/colorAccent"
            android:text="Button" />
    
    </android.support.constraint.motion.MotionLayout>
            
  3. สร้าง MotionScene: ในMotionLayout ตัวอย่างก่อนหน้า แอตทริบิวต์ app:layoutDescription อ้างอิงถึง MotionScene ฉากเคลื่อนไหวคือไฟล์ทรัพยากร XML ภายในองค์ประกอบรูทของ <MotionScene> ฉากการเคลื่อนไหวจะมีคำอธิบายการเคลื่อนไหวทั้งหมดสำหรับเลย์เอาต์ที่ เกี่ยวข้อง แต่ละ MotionLayout จะอ้างอิงฉากการเคลื่อนไหว แยกต่างหาก เพื่อแยกข้อมูลเลย์เอาต์ออกจากคำอธิบาย การเคลื่อนไหว คำจำกัดความในฉากการเคลื่อนไหวจะมีความสำคัญเหนือกว่าคำจำกัดความที่คล้ายกันใน MotionLayout

    ต่อไปนี้คือตัวอย่างไฟล์ฉากการเคลื่อนไหวที่อธิบายการเคลื่อนไหวแนวนอนพื้นฐาน ในรูปที่ 1

    <?xml version="1.0" encoding="utf-8"?>
    <MotionScene xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:motion="http://schemas.android.com/apk/res-auto">
    
        <Transition
            motion:constraintSetStart="@+id/start"
            motion:constraintSetEnd="@+id/end"
            motion:duration="1000">
            <OnSwipe
                motion:touchAnchorId="@+id/button"
                motion:touchAnchorSide="right"
                motion:dragDirection="dragRight" />
        </Transition>
    
        <ConstraintSet android:id="@+id/start">
            <Constraint
                android:id="@+id/button"
                android:layout_width="64dp"
                android:layout_height="64dp"
                android:layout_marginStart="8dp"
                motion:layout_constraintBottom_toBottomOf="parent"
                motion:layout_constraintStart_toStartOf="parent"
                motion:layout_constraintTop_toTopOf="parent" />
        </ConstraintSet>
    
        <ConstraintSet android:id="@+id/end">
            <Constraint
                android:id="@+id/button"
                android:layout_width="64dp"
                android:layout_height="64dp"
                android:layout_marginEnd="8dp"
                motion:layout_constraintBottom_toBottomOf="parent"
                motion:layout_constraintEnd_toEndOf="parent"
                motion:layout_constraintTop_toTopOf="parent" />
        </ConstraintSet>
    
    </MotionScene>
        

    ข้อควรทราบ

    • <Transition> มีคำจำกัดความพื้นฐานของการเคลื่อนไหว

      • motion:constraintSetStart และ motion:constraintSetEnd เป็นการอ้างอิงถึง จุดสิ้นสุดของการเคลื่อนไหว ปลายทางเหล่านี้กำหนดไว้ใน องค์ประกอบ <ConstraintSet> ในภายหลังในฉากการเคลื่อนไหว

      • motion:duration ระบุจำนวนมิลลิวินาที ที่ใช้ในการเคลื่อนไหวให้เสร็จสมบูรณ์

    • <OnSwipe> ช่วยให้คุณสร้างการควบคุมด้วยการสัมผัสสำหรับการเคลื่อนไหวได้

      • motion:touchAnchorId หมายถึงมุมมองที่ผู้ใช้สามารถ ปัดและลากได้

      • motion:touchAnchorSide หมายความว่า กำลังลากมุมมองจากด้านขวา

      • motion:dragDirection หมายถึงความคืบหน้า ทิศทางการลาก เช่น motion:dragDirection="dragRight" หมายถึงความคืบหน้า จะเพิ่มขึ้นเมื่อลากมุมมองไปทางขวา

    • <ConstraintSet> คือที่ที่คุณกำหนดข้อจำกัดต่างๆ ที่อธิบายการเคลื่อนไหว ในตัวอย่างนี้ มีการกำหนด <ConstraintSet> 1 รายการสำหรับ แต่ละจุดสิ้นสุดของการเคลื่อนไหว ปลายทางเหล่านี้อยู่ตรงกลางในแนวตั้ง โดยใช้ app:layout_constraintTop_toTopOf="parent" และ app:layout_constraintBottom_toBottomOf="parent" ในแนวนอน จุดสิ้นสุดจะอยู่ทางด้านซ้ายและขวาสุดของหน้าจอ

    ดูรายละเอียดเพิ่มเติมเกี่ยวกับองค์ประกอบต่างๆ ที่ฉากเคลื่อนไหวรองรับได้ที่ตัวอย่าง MotionLayout

แอตทริบิวต์ที่ประมาณค่า

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

  • alpha
  • visibility
  • elevation
  • rotation, rotationX, rotationY
  • translationX, translationY, translationZ
  • scaleX, scaleY

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

ภายใน <Constraint> คุณสามารถใช้องค์ประกอบ <CustomAttribute> เพื่อระบุ การเปลี่ยนสำหรับแอตทริบิวต์ที่ไม่ได้เกี่ยวข้องกับตำแหน่งหรือแอตทริบิวต์ View เพียงอย่างเดียว

<Constraint
    android:id="@+id/button" ...>
    <CustomAttribute
        motion:attributeName="backgroundColor"
        motion:customColorValue="#D81B60"/>
</Constraint>

<CustomAttribute> มีแอตทริบิวต์ของตัวเอง 2 รายการ ได้แก่

  • motion:attributeName ต้องระบุและต้องตรงกับออบเจ็กต์ที่มีเมธอด Getter และ Setter Getter และ Setter ต้องตรงกับรูปแบบที่เฉพาะเจาะจง ตัวอย่างเช่น ระบบรองรับ backgroundColor เนื่องจากมุมมองมีเมธอด getBackgroundColor() และ setBackgroundColor() ที่เกี่ยวข้อง
  • แอตทริบิวต์อื่นๆ ที่คุณต้องระบุจะขึ้นอยู่กับประเภทค่า เลือกจากประเภทที่รองรับต่อไปนี้
    • motion:customColorValue สำหรับสี
    • motion:customIntegerValue สำหรับจำนวนเต็ม
    • motion:customFloatValue สำหรับการลอย
    • motion:customStringValue สำหรับสตริง
    • motion:customDimension สำหรับมิติข้อมูล
    • motion:customBoolean สำหรับบูลีน

เมื่อระบุแอตทริบิวต์ที่กำหนดเอง ให้กำหนดค่าปลายทางทั้งในองค์ประกอบเริ่มต้นและองค์ประกอบสิ้นสุด <ConstraintSet>

เปลี่ยนสีพื้นหลัง

จากตัวอย่างก่อนหน้า สมมติว่าคุณต้องการให้สีของวิวเปลี่ยน เป็นส่วนหนึ่งของการเคลื่อนไหว ดังที่แสดงในรูปที่ 2

รูปที่ 2 มุมมองจะเปลี่ยนสีพื้นหลังเมื่อเคลื่อนที่

เพิ่มองค์ประกอบ <CustomAttribute> ลงในองค์ประกอบ ConstraintSet แต่ละรายการ ดังที่แสดงในข้อมูลโค้ดต่อไปนี้

<ConstraintSet android:id="@+id/start">
    <Constraint
        android:id="@+id/button"
        android:layout_width="64dp"
        android:layout_height="64dp"
        android:layout_marginStart="8dp"
        motion:layout_constraintBottom_toBottomOf="parent"
        motion:layout_constraintStart_toStartOf="parent"
        motion:layout_constraintTop_toTopOf="parent">
        <CustomAttribute
            motion:attributeName="backgroundColor"
            motion:customColorValue="#D81B60" />
    </Constraint>
</ConstraintSet>

<ConstraintSet android:id="@+id/end">
    <Constraint
        android:id="@+id/button"
        android:layout_width="64dp"
        android:layout_height="64dp"
        android:layout_marginEnd="8dp"
        motion:layout_constraintBottom_toBottomOf="parent"
        motion:layout_constraintEnd_toEndOf="parent"
        motion:layout_constraintTop_toTopOf="parent">
        <CustomAttribute
            motion:attributeName="backgroundColor"
            motion:customColorValue="#9999FF" />
    </Constraint>
</ConstraintSet>

แอตทริบิวต์ MotionLayout เพิ่มเติม

นอกจากแอตทริบิวต์ในตัวอย่างก่อนหน้าแล้ว MotionLayout ยังมีแอตทริบิวต์อื่นๆ ที่คุณอาจต้องการระบุด้วย

  • app:applyMotionScene="boolean" ระบุว่าจะใช้ฉากเคลื่อนไหวหรือไม่ ค่าเริ่มต้นสำหรับแอตทริบิวต์นี้คือ true
  • app:showPaths="boolean" ระบุว่าจะแสดงเส้นทางการเคลื่อนไหวขณะที่ การเคลื่อนไหวทำงานอยู่หรือไม่ ค่าเริ่มต้นสำหรับแอตทริบิวต์นี้คือ false
  • app:progress="float" ช่วยให้คุณระบุความคืบหน้าของการเปลี่ยนฉากได้อย่างชัดเจน คุณ สามารถใช้ค่าจุดลอยตัวใดก็ได้ตั้งแต่ 0 (จุดเริ่มต้นของการเปลี่ยนผ่าน) ไปจนถึง 1 (จุดสิ้นสุดของการเปลี่ยนผ่าน)
  • app:currentState="reference" ช่วยให้คุณระบุ ConstraintSet ที่เฉพาะเจาะจงได้
  • app:motionDebug ช่วยให้คุณแสดงข้อมูลการแก้ไขข้อบกพร่องเพิ่มเติมเกี่ยวกับ การเคลื่อนไหวได้ ค่าที่เป็นไปได้คือ "SHOW_PROGRESS", "SHOW_PATH" หรือ "SHOW_ALL"

แหล่งข้อมูลเพิ่มเติม

ดูข้อมูลเพิ่มเติมเกี่ยวกับ MotionLayout ได้ที่แหล่งข้อมูลต่อไปนี้