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

Thử cách Compose
Jetpack Compose là bộ công cụ giao diện người dùng được đề xuất cho Android. Tìm hiểu cách sử dụng ảnh động trong Compose.

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

MotionLayout thu hẹp khoảng cách giữa các hiệu ứng chuyển đổi bố cục và việc xử lý chuyển động phức tạp, cung cấp nhiều tính năng giữa khung ảnh động thuộc tính, TransitionManagerCoordinatorLayout.

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

Ngoài việc mô tả các hiệu ứng chuyển đổi giữa các bố cục, MotionLayout còn cho phép bạn tạo hiệu ứng động cho mọi thuộc tính bố cục. Ngoài ra, 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ể ngay lập tức hiện bất kỳ điểm nào trong quá trình chuyển đổi dựa trên một số điều kiện, chẳng hạn như thao tác chạm. MotionLayout cũng hỗ trợ các khung hình chính, cho phép bạn tuỳ chỉnh hoàn toàn các hiệu ứng chuyển đổi cho phù hợp với nhu cầu của mình.

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

Cân nhắc về thiết kế

MotionLayout được dùng để di chuyển, thay đổ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 tương tác, chẳng hạn như nút và thanh tiêu đề. Đừng dùng chuyển động trong ứng dụng của bạn như một hiệu ứng đặc biệt không cần thiết. Hãy dùng thông báo này để giúp người dùng hiểu được những việc mà ứng dụng của bạn đang làm. Để 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

Hãy làm theo các bước sau để bắt đầu sử dụng MotionLayout trong dự án của bạ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 sử dụng AndroidX, hãy thêm phần phụ thuộc sau:

    Groovy

    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. Tạo tệp MotionLayout: MotionLayout là một 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 hoạ trong các 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, xác định bố cục xuất hiện 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ụ 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 biệt. Các định nghĩa trong cảnh chuyển động sẽ được ưu tiên hơn mọi định nghĩa tương tự trong MotionLayout.

    Sau đây là ví dụ về tệp cảnh chuyển động 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ơ bản về chuyển động.

      • motion:constraintSetStartmotion:constraintSetEnd là các tham chiếu đến đ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> sau trong cảnh chuyển động.

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

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

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

      • motion:touchAnchorSide có nghĩa là khung hiển thị đang được kéo từ phía 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 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 ràng buộc khác nhau mô tả chuyển động của bạn. 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 cách sử dụ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 bên trái và bên phải của màn hình.

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

Thuộc tính được 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à ranh giới, MotionLayout còn nội suy các thuộc tính sau:

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

Thuộc tính tuỳ chỉnh

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

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

<CustomAttribute> chứa 2 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 có 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ì khung hiển thị có các phương thức getBackgroundColor()setBackgroundColor() cơ bản.
  • Thuộc tính khác 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 đối với số nguyên
    • motion:customFloatValue cho số thực
    • motion:customStringValue cho chuỗi
    • motion:customDimension cho phương diện
    • motion:customBoolean cho giá trị boolean

Khi chỉ định một thuộc tính tùy chỉnh, hãy xác định các giá trị điểm cuối trong 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 sắc của khung hiển thị thay đổi trong quá trình chuyển động, 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:

<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 bổ sung của MotionLayout

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 có thể muố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 có hiển thị đường 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 bất kỳ giá trị dấu phẩy động nào từ 0 (bắt đầu 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. Các 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: