Quản lý ảnh động chuyển động và tiện ích bằng MotionLayout

MotionLayout là một loại bố cục giúp bạn quản lý ảnh động chuyển động và tiện ích trong ứng dụng của mình. MotionLayout là một lớp con của ConstraintLayout và được xây dựng dựa trên các tính năng bố cục phong phú. Là một phần của thư viện ConstraintLayout, MotionLayout được cung cấp dưới dạng thư viện hỗ trợ.

MotionLayout là cầu nối giữa việc chuyển đổi bố cục và xử lý chuyển động phức tạp, cung cấp sự kết hợp các tính năng giữa khung ảnh động thuộc tính, TransitionManagerCoordinatorLayout.

Hình 1. Chuyển động cơ bản điều khiển bằng cách chạm.

Ngoài việc mô tả hiệu ứng chuyển đổi giữa các bố cục, MotionLayout còn cho phép bạn tạo ảnh động cho bất kỳ thuộc tính bố cục nào. Hơn nữa, nó vốn hỗ trợ các hiệu ứng chuyển đổi có thể tìm kiếm. Điều này có nghĩa là bạn có thể hiển thị ngay lập tức bất kỳ điểm nào trong hiệu ứng chuyển đổi dựa trên một số điều kiện, chẳng hạn như nhập bằng cách chạm. MotionLayout cũng hỗ trợ các khung hình chính, cho phép các hiệu ứng chuyển đổi được tuỳ chỉnh hoàn toàn cho phù hợp với nhu cầu của bạn.

MotionLayout mang tính khai báo đầy đủ, nghĩa là bạn có thể mô tả mọi quá trình chuyển đổi trong XML, bất kể phức tạp đến đâu.

Những điều cần lưu ý khi thiết kế

MotionLayout dùng để di chuyển, đổi kích thước và tạo ảnh động cho các phần tử trên giao diện người dùng mà người dùng có thể tương tác, chẳng hạn như các nút và thanh tiêu đề. Đừng sử dụng chuyển động trong ứng dụng như một hiệu ứng đặc biệt vô cớ. Hãy sử dụng báo cáo này để giúp người dùng hiểu ứng dụng của bạn đang làm gì. Để biết thêm thông tin về cách thiết kế ứng dụng bằng chuyển động, hãy xem phần Tìm hiểu về chuyển động trong Material Design.

Bắt đầu

Làm theo các bước sau để bắt đầu sử dụng MotionLayout trong dự án.

  1. Thêm phần phụ thuộc ConstraintLayout: để sử dụng MotionLayout trong dự án, hãy thêm phần phụ thuộc ConstraintLayout 2.0 vào tệp build.gradle của ứng dụng. Nếu bạn đang dùng AndroidX, hãy thêm phần phụ thuộc sau:

    Groovy

    dependencies {
        implementation "androidx.constraintlayout:constraintlayout:2.2.0-alpha07"
        // To use constraintlayout in compose
        implementation "androidx.constraintlayout:constraintlayout-compose:1.1.0-alpha07"
    }
    

    Kotlin

    dependencies {
        implementation("androidx.constraintlayout:constraintlayout:2.2.0-alpha07")
        // To use constraintlayout in compose
        implementation("androidx.constraintlayout:constraintlayout-compose:1.1.0-alpha07")
    }
    
  2. Tạo tệp MotionLayout: MotionLayout là lớp con của ConstraintLayout, vì vậy, bạn có thể chuyển đổi mọi ConstraintLayout hiện có thành MotionLayout bằng cách thay thế tên lớp trong tệp tài nguyên bố cục, như minh họa trong ví dụ sau:

    AndroidX

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

    Thư viện hỗ trợ

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

    Dưới đây là ví dụ đầy đủ về tệp MotionLayout. Tệp này xác định bố cục như trong hình 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>
            

    Thư viện hỗ trợ

    <?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. Tạo MotionScene: trong ví dụ về MotionLayout trước, thuộc tính app:layoutDescription tham chiếu đến một cảnh chuyển động. Cảnh chuyển động là một tệp tài nguyên XML. Trong phần tử gốc <MotionScene>, một cảnh chuyển động chứa tất cả nội dung mô tả chuyển động cho bố cục tương ứng. Để tách biệt thông tin bố cục với nội dung mô tả chuyển động, mỗi MotionLayout sẽ tham chiếu đến một cảnh chuyển động riêng. Các định nghĩa trong cảnh chuyển động được ưu tiên hơn mọi định nghĩa tương tự trong MotionLayout.

    Dưới đây là tệp cảnh chuyển động mẫu mô tả chuyển động ngang cơ bản trong hình 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>
        

    Xin lưu ý những điều sau:

    • <Transition> chứa định nghĩa cơ sở của chuyển động.

      • motion:constraintSetStartmotion:constraintSetEnd là các tham chiếu đến các điểm cuối của chuyển động. Các điểm cuối này được xác định trong các phần tử <ConstraintSet> ở phần sau của cảnh chuyển động.

      • motion:duration chỉ định số mili giây để chuyển động hoàn tất.

    • <OnSwipe> cho phép bạn tạo chế độ điều khiển cảm ứng cho chuyển động.

      • motion:touchAnchorId đề cập đến thành phần hiển thị mà người dùng có thể vuốt và kéo.

      • motion:touchAnchorSide có nghĩa là thành phần hiển thị đang được kéo từ bên phải.

      • motion:dragDirection đề cập đến hướng tiến trình của thao tác kéo. Ví dụ: motion:dragDirection="dragRight" có nghĩa là tiến trình sẽ tăng lên khi khung hiển thị được kéo sang phải.

    • <ConstraintSet> là nơi bạn xác định các quy tắc ràng buộc mô tả chuyển động của mình. Trong ví dụ này, một <ConstraintSet> được xác định cho mỗi điểm cuối của chuyển động. Các điểm cuối này được căn giữa theo chiều dọc bằng app:layout_constraintTop_toTopOf="parent"app:layout_constraintBottom_toBottomOf="parent". Theo chiều ngang, các điểm cuối nằm ở phía xa bên trái và bên phải màn hình.

    Để biết thông tin chi tiết hơn về các phần tử mà cảnh chuyển động hỗ trợ, hãy xem các ví dụ về MotionLayout.

Thuộc tính nội suy

Trong tệp cảnh chuyển động, các phần tử ConstraintSet có thể chứa các thuộc tính bổ sung được nội suy trong quá trình chuyển đổi. Ngoài vị trí và giới hạn, các thuộc tính sau đây còn được nội suy bằng MotionLayout:

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

Thuộc tính tùy chỉnh

Trong <Constraint>, bạn có thể sử dụng phần tử <CustomAttribute> để chỉ định hiệu ứng chuyển đổi cho các thuộc tính không chỉ liên quan đến vị trí hoặc các thuộc tính View.

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

<CustomAttribute> chứa hai thuộc tính riêng:

  • motion:attributeName là bắt buộc và phải khớp với một đối tượng bằng phương thức getter và setter. Phương thức getter và setter phải khớp với một mẫu cụ thể. Ví dụ: backgroundColor được hỗ trợ vì thành phần hiển thị này có các phương thức getBackgroundColor()setBackgroundColor() cơ bản.
  • Thuộc tính còn lại mà bạn phải cung cấp dựa trên loại giá trị. Chọn trong số các loại được hỗ trợ sau:
    • motion:customColorValue cho màu sắc
    • motion:customIntegerValue cho số nguyên
    • motion:customFloatValue cho phao nổi
    • motion:customStringValue cho chuỗi
    • motion:customDimension cho các phương diện
    • motion:customBoolean cho giá trị boolean

Khi chỉ định một thuộc tính tuỳ chỉnh, hãy xác định các giá trị điểm cuối ở cả phần tử <ConstraintSet> bắt đầu và kết thúc.

Thay đổi màu nền

Dựa trên ví dụ trước, giả sử bạn muốn màu của khung hiển thị thay đổi như một phần trong chuyển động của khung hiển thị, như minh hoạ trong hình 2.

Hình 2. Khung hiển thị sẽ thay đổi màu nền khi di chuyển.

Thêm một phần tử <CustomAttribute> vào mỗi phần tử ConstraintSet, như minh hoạ trong đoạn mã sau đây:

<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>

Các thuộc tính MotionLayout khác

Ngoài các thuộc tính trong ví dụ trước, MotionLayout còn có các thuộc tính khác mà bạn nên chỉ định:

  • app:applyMotionScene="boolean" cho biết có áp dụng cảnh chuyển động hay không. Giá trị mặc định của thuộc tính này là true.
  • app:showPaths="boolean" cho biết liệu có hiển thị các đường dẫn chuyển động khi chuyển động đang chạy hay không. Giá trị mặc định của thuộc tính này là false.
  • app:progress="float" cho phép bạn chỉ định rõ tiến trình chuyển đổi. Bạn có thể sử dụng giá trị dấu phẩy động bất kỳ từ 0 (điểm bắt đầu của quá trình chuyển đổi) đến 1 (kết thúc quá trình chuyển đổi).
  • app:currentState="reference" cho phép bạn chỉ định một ConstraintSet cụ thể.
  • app:motionDebug cho phép bạn hiển thị thông tin gỡ lỗi bổ sung về chuyển động. Giá trị có thể là "SHOW_PROGRESS", "SHOW_PATH" hoặc "SHOW_ALL".

Tài nguyên khác

Để biết thêm thông tin về MotionLayout, hãy xem các tài nguyên sau: