Tạo ảnh động cho các thay đổi về bố cục bằng khung chuyển đổi

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.

Khung chuyển đổi của Android cho phép bạn tạo ảnh động cho mọi loại chuyển động trong giao diện người dùng bằng cách cung cấp bố cục bắt đầu và kết thúc. Bạn có thể chọn loại ảnh động mà mình muốn (chẳng hạn như làm mờ các khung hiển thị hoặc thay đổi kích thước khung hiển thị) và khung chuyển đổi sẽ xác định cách tạo ảnh động từ bố cục bắt đầu đến bố cục kết thúc.

Khung chuyển đổi bao gồm các tính năng sau:

  • Ảnh động cấp nhóm: áp dụng hiệu ứng ảnh động cho tất cả các khung hiển thị trong một hệ phân cấp view.
  • Ảnh động tích hợp: sử dụng ảnh động được xác định trước cho các hiệu ứng phổ biến như làm mờ hoặc chuyển động.
  • Hỗ trợ tệp tài nguyên: tải hệ phân cấp khung hiển thị và các ảnh động tích hợp sẵn từ tệp tài nguyên bố cục.
  • Lệnh gọi lại trong vòng đời: nhận các lệnh gọi lại giúp kiểm soát quá trình thay đổi ảnh động và hệ phân cấp.

Để xem mã mẫu tạo ảnh động giữa các thay đổi về bố cục, hãy xem BasicTransition.

Quy trình cơ bản để tạo ảnh động giữa hai bố cục như sau:

  1. Tạo một đối tượng Scene cho bố cục bắt đầu và kết thúc. Tuy nhiên, cảnh trong bố cục ban đầu thường được xác định tự động từ bố cục hiện tại.
  2. Tạo một đối tượng Transition để xác định loại ảnh động mà bạn muốn.
  3. Gọi TransitionManager.go() và hệ thống sẽ chạy ảnh động để hoán đổi bố cục.

Sơ đồ trong hình 1 minh hoạ mối quan hệ giữa bố cục, cảnh, hiệu ứng chuyển đổi và ảnh động cuối cùng.

Hình 1. Hình minh hoạ cơ bản về cách khung chuyển đổi tạo ảnh động.

Tạo cảnh

Cảnh lưu trữ trạng thái của một hệ phân cấp view, bao gồm tất cả các view và giá trị thuộc tính của view đó. Khung chuyển đổi có thể chạy ảnh động giữa cảnh bắt đầu và cảnh kết thúc.

Bạn có thể tạo cảnh từ tệp tài nguyên bố cục hoặc từ một nhóm khung hiển thị trong mã. Tuy nhiên, cảnh bắt đầu cho hiệu ứng chuyển đổi thường được xác định tự động từ giao diện người dùng hiện tại.

Một cảnh cũng có thể xác định các thao tác riêng sẽ chạy khi bạn thực hiện thay đổi cảnh. Tính năng này hữu ích khi bạn dọn dẹp các chế độ cài đặt khung hiển thị sau khi chuyển cảnh.

Tạo cảnh từ một tài nguyên bố cục

Bạn có thể tạo một thực thể Scene ngay từ tệp tài nguyên bố cục. Sử dụng kỹ thuật này khi hệ phân cấp khung hiển thị trong tệp chủ yếu là tĩnh. Cảnh thu được thể hiện trạng thái của hệ phân cấp view tại thời điểm bạn tạo thực thể Scene. Nếu bạn thay đổi hệ phân cấp view, hãy tạo lại cảnh. Khung này tạo cảnh từ toàn bộ hệ phân cấp khung hiển thị trong tệp. Bạn không thể tạo một cảnh từ một phần của tệp bố cục.

Để tạo một thực thể Scene từ tệp tài nguyên bố cục, hãy truy xuất gốc cảnh từ bố cục dưới dạng ViewGroup. Sau đó, hãy gọi hàm Scene.getSceneForLayout() bằng gốc cảnh và mã nhận dạng tài nguyên của tệp bố cục chứa hệ phân cấp view cho cảnh.

Xác định bố cục cho các cảnh

Các đoạn mã trong phần còn lại của phần này cho biết cách tạo 2 cảnh khác nhau có cùng phần tử gốc cảnh. Các đoạn mã này cũng minh hoạ rằng bạn có thể tải nhiều đối tượng Scene không liên quan mà không ngụ ý rằng chúng có liên quan đến nhau.

Ví dụ này bao gồm các định nghĩa bố cục sau:

  • Bố cục chính của một hoạt động có nhãn văn bản và FrameLayout con.
  • Một ConstraintLayout cho cảnh đầu tiên có hai trường văn bản.
  • Một ConstraintLayout cho cảnh thứ hai có cùng hai trường văn bản nhưng theo thứ tự khác.

Ví dụ này được thiết kế sao cho tất cả ảnh động đều diễn ra trong bố cục con của bố cục chính cho hoạt động. Nhãn văn bản trong bố cục chính vẫn ở trạng thái tĩnh.

Bố cục chính cho hoạt động được xác định như sau:

res/layout/activity_main.xml

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/master_layout">
    <TextView
        android:id="@+id/title"
        ...
        android:text="Title"/>
    <FrameLayout
        android:id="@+id/scene_root">
        <include layout="@layout/a_scene" />
    </FrameLayout>
</LinearLayout>

Định nghĩa bố cục này chứa một trường văn bản và một FrameLayout con cho gốc cảnh. Bố cục cho cảnh đầu tiên có trong tệp bố cục chính. Điều này cho phép ứng dụng hiển thị thành phần đó trong giao diện người dùng ban đầu và cũng cho phép tải thành phần đó vào một cảnh, vì khung chỉ có thể tải toàn bộ tệp bố cục vào một cảnh.

Bố cục cho cảnh đầu tiên được xác định như sau:

res/layout/a_scene.xml

<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:id="@+id/scene_container"
    android:layout_width="match_parent"
    android:layout_height="match_parent" >
    <TextView
        android:id="@+id/text_view1"
        android:layout_height="wrap_content"
        android:layout_width="wrap_content"
        android:text="Text Line 1"
        app:layout_constraintTop_toTopOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintEnd_toEndOf="parent"/>
    <TextView
        android:id="@+id/text_view2"
        android:layout_height="wrap_content"
        android:layout_width="wrap_content"
        android:text="Text Line 2"
        app:layout_constraintTop_toBottomOf="@id/text_view1"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintEnd_toEndOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>

Bố cục cho cảnh thứ hai chứa cùng 2 trường văn bản (có cùng mã nhận dạng) được đặt theo một thứ tự khác. Được xác định như sau:

res/layout/another_scene.xml

<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:id="@+id/scene_container"
    android:layout_width="match_parent"
    android:layout_height="match_parent" >
    <TextView
        android:id="@+id/text_view2"
        android:layout_height="wrap_content"
        android:layout_width="wrap_content"
        android:text="Text Line 2"
        app:layout_constraintTop_toTopOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintEnd_toEndOf="parent" />
    <TextView
        android:id="@+id/text_view1"
        android:layout_height="wrap_content"
        android:layout_width="wrap_content"
        android:text="Text Line 1"
        app:layout_constraintTop_toBottomOf="@id/text_view2"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintEnd_toEndOf="parent"/>
</androidx.constraintlayout.widget.ConstraintLayout>

Tạo cảnh từ bố cục

Sau khi tạo định nghĩa cho hai bố cục ràng buộc, bạn có thể lấy một cảnh cho từng bố cục. Điều này cho phép bạn chuyển đổi giữa hai cấu hình giao diện người dùng. Để lấy một cảnh, bạn cần có một tham chiếu đến gốc cảnh và mã nhận dạng tài nguyên bố cục.

Đoạn mã sau đây cho biết cách lấy một tham chiếu đến gốc cảnh và tạo 2 đối tượng Scene từ các tệp bố cục:

Kotlin

val sceneRoot: ViewGroup = findViewById(R.id.scene_root)
val aScene: Scene = Scene.getSceneForLayout(sceneRoot, R.layout.a_scene, this)
val anotherScene: Scene = Scene.getSceneForLayout(sceneRoot, R.layout.another_scene, this)

Java

Scene aScene;
Scene anotherScene;

// Create the scene root for the scenes in this app.
sceneRoot = (ViewGroup) findViewById(R.id.scene_root);

// Create the scenes.
aScene = Scene.getSceneForLayout(sceneRoot, R.layout.a_scene, this);
anotherScene =
    Scene.getSceneForLayout(sceneRoot, R.layout.another_scene, this);

Trong ứng dụng, hiện có 2 đối tượng Scene dựa trên các hệ phân cấp khung hiển thị. Cả hai cảnh đều sử dụng gốc cảnh do phần tử FrameLayout xác định trong res/layout/activity_main.xml.

Tạo một cảnh trong mã

Bạn cũng có thể tạo một thực thể Scene trong mã của mình từ một đối tượng ViewGroup. Hãy sử dụng kỹ thuật này khi bạn sửa đổi trực tiếp các hệ phân cấp khung hiển thị trong mã hoặc khi bạn tạo các hệ phân cấp đó một cách linh hoạt.

Để tạo một cảnh từ hệ phân cấp view trong mã, hãy sử dụng hàm khởi tạo Scene(sceneRoot, viewHierarchy). Việc gọi hàm khởi tạo này tương đương với việc gọi hàm Scene.getSceneForLayout() khi bạn đã mở rộng một tệp bố cục.

Đoạn mã sau đây minh hoạ cách tạo một thực thể Scene từ phần tử gốc của cảnh và hệ phân cấp view cho cảnh trong mã của bạn:

Kotlin

val sceneRoot = someLayoutElement as ViewGroup
val viewHierarchy = someOtherLayoutElement as ViewGroup
val scene: Scene = Scene(sceneRoot, viewHierarchy)

Java

Scene mScene;

// Obtain the scene root element.
sceneRoot = (ViewGroup) someLayoutElement;

// Obtain the view hierarchy to add as a child of
// the scene root when this scene is entered.
viewHierarchy = (ViewGroup) someOtherLayoutElement;

// Create a scene.
mScene = new Scene(sceneRoot, mViewHierarchy);

Tạo thao tác trong cảnh

Khung này cho phép bạn xác định các thao tác tuỳ chỉnh trên cảnh mà hệ thống sẽ chạy khi vào hoặc thoát khỏi một cảnh. Trong nhiều trường hợp, bạn không cần xác định các thao tác tuỳ chỉnh cho cảnh vì khung hiển thị sẽ tự động tạo ảnh động cho sự thay đổi giữa các cảnh.

Các thao tác trên cảnh rất hữu ích khi xử lý những trường hợp sau:

  • Để tạo ảnh động cho các khung hiển thị không nằm trong cùng một hệ phân cấp. Bạn có thể tạo ảnh động cho các khung hiển thị của cảnh bắt đầu và kết thúc bằng cách sử dụng các thao tác thoát và vào cảnh.
  • Để tạo hiệu ứng cho những khung hiển thị mà khung chuyển đổi không thể tự động tạo hiệu ứng, chẳng hạn như các đối tượng ListView. Để biết thêm thông tin, hãy xem phần các điểm hạn chế.

Để cung cấp các thao tác tuỳ chỉnh cho cảnh, hãy xác định các thao tác của bạn dưới dạng đối tượng Runnable và truyền các đối tượng đó đến hàm Scene.setExitAction() hoặc Scene.setEnterAction(). Khung này gọi hàm setExitAction() trên cảnh bắt đầu trước khi chạy ảnh động chuyển đổi và hàm setEnterAction() trên cảnh kết thúc sau khi chạy ảnh động chuyển đổi.

Áp dụng hiệu ứng chuyển cảnh

Khung chuyển đổi biểu thị kiểu ảnh động giữa các cảnh bằng một đối tượng Transition. Bạn có thể tạo một thực thể Transition bằng cách sử dụng các lớp con tích hợp, chẳng hạn như AutoTransitionFade, hoặc xác định hiệu ứng chuyển đổi của riêng bạn. Sau đó, bạn có thể chạy ảnh động giữa các cảnh bằng cách truyền SceneTransition cuối cùng đến TransitionManager.go().

Vòng đời chuyển đổi tương tự như vòng đời hoạt động và thể hiện các trạng thái chuyển đổi mà khung theo dõi giữa thời điểm bắt đầu và hoàn thành một ảnh động. Ở các trạng thái quan trọng trong vòng đời, khung sẽ gọi các hàm gọi lại mà bạn có thể triển khai để điều chỉnh giao diện người dùng ở các giai đoạn chuyển đổi khác nhau.

Tạo hiệu ứng chuyển cảnh

Phần trước cho biết cách tạo các cảnh thể hiện trạng thái của các hệ phân cấp khung hiển thị khác nhau. Sau khi xác định cảnh bắt đầu và cảnh kết thúc mà bạn muốn thay đổi, hãy tạo một đối tượng Transition xác định một ảnh động. Khung này cho phép bạn chỉ định một hiệu ứng chuyển đổi tích hợp trong tệp tài nguyên và tăng kích thước hiệu ứng đó trong mã hoặc tạo một phiên bản của hiệu ứng chuyển đổi tích hợp ngay trong mã.

Bảng 1. Các loại hiệu ứng chuyển cảnh tích hợp sẵn.

Lớp Thẻ Hiệu ứng
AutoTransition <autoTransition/> Chuyển cảnh mặc định. Lần lượt làm mờ, di chuyển và đổi kích thước, rồi làm mờ dần các khung hiển thị.
ChangeBounds <changeBounds/> Di chuyển và đổi kích thước khung hiển thị.
ChangeClipBounds <changeClipBounds/> Ghi lại View.getClipBounds() trước và sau khi thay đổi cảnh, đồng thời tạo hiệu ứng cho những thay đổi đó trong quá trình chuyển cảnh.
ChangeImageTransform <changeImageTransform/> Nắm bắt ma trận của một ImageView trước và sau khi thay đổi cảnh, đồng thời tạo hiệu ứng cho ma trận đó trong quá trình chuyển cảnh.
ChangeScroll <changeScroll/> Ghi lại các thuộc tính cuộn của mục tiêu trước và sau khi thay đổi cảnh, đồng thời tạo hiệu ứng cho mọi thay đổi.
ChangeTransform <changeTransform/> Ghi lại tỷ lệ và hướng xoay của các khung hiển thị trước và sau khi thay đổi cảnh, đồng thời tạo hiệu ứng cho những thay đổi đó trong quá trình chuyển đổi.
Explode <explode/> Theo dõi các thay đổi về chế độ hiển thị của khung hiển thị đích trong cảnh bắt đầu và kết thúc, đồng thời di chuyển khung hiển thị vào hoặc ra khỏi các cạnh của cảnh.
Fade <fade/> fade_in mờ dần trong các khung hiển thị.
fade_out làm mờ các khung hiển thị.
fade_in_out (mặc định) thực hiện fade_out, sau đó là fade_in.
Slide <slide/> Theo dõi các thay đổi về chế độ hiển thị của khung hiển thị đích trong cảnh bắt đầu và kết thúc, đồng thời di chuyển khung hiển thị vào hoặc ra khỏi một trong các cạnh của cảnh.

Tạo một thực thể chuyển đổi từ tệp tài nguyên

Kỹ thuật này cho phép bạn sửa đổi định nghĩa chuyển đổi mà không cần thay đổi mã của hoạt động. Kỹ thuật này cũng hữu ích để tách các định nghĩa chuyển đổi phức tạp khỏi mã ứng dụng của bạn, như minh hoạ trong phần về chỉ định nhiều hiệu ứng chuyển đổi.

Để chỉ định một hiệu ứng chuyển đổi tích hợp sẵn trong tệp tài nguyên, hãy làm theo các bước sau:

  • Thêm thư mục res/transition/ vào dự án của bạn.
  • Tạo một tệp tài nguyên XML mới trong thư mục này.
  • Thêm một nút XML cho một trong các hiệu ứng chuyển cảnh tích hợp.

Ví dụ: tệp tài nguyên sau đây chỉ định hiệu ứng chuyển đổi Fade:

res/transition/fade_transition.xml

<fade xmlns:android="http://schemas.android.com/apk/res/android" />

Đoạn mã sau đây cho biết cách tăng một thực thể Transition bên trong hoạt động của bạn từ một tệp tài nguyên:

Kotlin

var fadeTransition: Transition =
    TransitionInflater.from(this)
                      .inflateTransition(R.transition.fade_transition)

Java

Transition fadeTransition =
        TransitionInflater.from(this).
        inflateTransition(R.transition.fade_transition);

Tạo một thực thể chuyển đổi trong mã

Kỹ thuật này rất hữu ích khi tạo các đối tượng chuyển đổi một cách linh động nếu bạn sửa đổi giao diện người dùng trong mã và tạo các thực thể chuyển đổi tích hợp đơn giản với ít hoặc không có tham số.

Để tạo một thực thể của hiệu ứng chuyển đổi tích hợp, hãy gọi một trong các hàm khởi tạo công khai trong các lớp con của lớp Transition. Ví dụ: đoạn mã sau đây tạo một phiên bản của hiệu ứng chuyển đổi Fade:

Kotlin

var fadeTransition: Transition = Fade()

Java

Transition fadeTransition = new Fade();

Áp dụng hiệu ứng chuyển cảnh

Thông thường, bạn sẽ áp dụng một hiệu ứng chuyển cảnh để thay đổi giữa các hệ phân cấp view khác nhau nhằm phản hồi một sự kiện, chẳng hạn như hành động của người dùng. Ví dụ: hãy xem xét một ứng dụng tìm kiếm: khi người dùng nhập một cụm từ tìm kiếm và nhấn vào nút tìm kiếm, ứng dụng sẽ thay đổi thành một cảnh đại diện cho bố cục kết quả trong khi áp dụng một hiệu ứng chuyển cảnh làm mờ nút tìm kiếm và làm mờ kết quả tìm kiếm.

Để thay đổi cảnh trong khi áp dụng một hiệu ứng chuyển cảnh để phản hồi một sự kiện trong Hoạt động của bạn, hãy gọi hàm lớp TransitionManager.go() bằng cảnh kết thúc và thực thể hiệu ứng chuyển cảnh để dùng cho ảnh động, như minh hoạ trong đoạn mã sau:

Kotlin

TransitionManager.go(endingScene, fadeTransition)

Java

TransitionManager.go(endingScene, fadeTransition);

Khung này sẽ thay đổi hệ phân cấp view bên trong gốc cảnh bằng hệ phân cấp thành phần hiển thị từ cảnh kết thúc trong khi chạy ảnh động do phiên bản chuyển đổi chỉ định. Cảnh bắt đầu là cảnh kết thúc của lần chuyển cảnh gần đây nhất. Nếu không có hiệu ứng chuyển đổi nào trước đó, cảnh bắt đầu sẽ được xác định tự động từ trạng thái hiện tại của giao diện người dùng.

Nếu bạn không chỉ định một thực thể chuyển đổi, trình quản lý chuyển đổi có thể áp dụng một hiệu ứng chuyển đổi tự động làm điều gì đó hợp lý cho hầu hết các trường hợp. Để biết thêm thông tin, hãy xem tài liệu tham khảo API cho lớp TransitionManager.

Chọn những khung hiển thị mục tiêu cụ thể

Theo mặc định, khung này sẽ áp dụng các hiệu ứng chuyển đổi cho tất cả khung hiển thị trong cảnh bắt đầu và kết thúc. Trong một số trường hợp, bạn chỉ muốn áp dụng một ảnh động cho một nhóm nhỏ các khung hiển thị trong một cảnh. Khung này cho phép bạn chọn những khung hiển thị cụ thể mà bạn muốn tạo hiệu ứng chuyển động. Ví dụ: khung này không hỗ trợ tạo ảnh động cho các thay đổi đối với đối tượng ListView, vì vậy, đừng cố gắng tạo ảnh động cho các đối tượng đó trong quá trình chuyển đổi.

Mỗi khung hiển thị mà hiệu ứng chuyển đổi tạo ảnh động được gọi là mục tiêu. Bạn chỉ có thể chọn các mục tiêu thuộc hệ phân cấp view được liên kết với một cảnh.

Để xoá một hoặc nhiều khung hiển thị khỏi danh sách đích, hãy gọi phương thức removeTarget() trước khi bắt đầu quá trình chuyển đổi. Để chỉ thêm những khung hiển thị mà bạn chỉ định vào danh sách đích, hãy gọi hàm addTarget(). Để biết thêm thông tin, hãy xem tài liệu tham khảo API cho lớp Transition.

Chỉ định nhiều hiệu ứng chuyển cảnh

Để có được hiệu ứng mạnh mẽ nhất từ một ảnh động, hãy điều chỉnh ảnh động đó cho phù hợp với loại thay đổi xảy ra giữa các cảnh. Ví dụ: nếu bạn đang xoá một số khung hiển thị và thêm các khung hiển thị khác giữa các cảnh, thì ảnh động mờ dần đi hoặc mờ dần hiện ra sẽ cho thấy rõ rằng một số khung hiển thị không còn dùng được nữa. Nếu bạn đang di chuyển khung hiển thị đến các điểm khác nhau trên màn hình, thì tốt hơn là bạn nên tạo hiệu ứng cho chuyển động để người dùng nhận thấy vị trí mới của khung hiển thị.

Bạn không chỉ phải chọn một ảnh động, vì khung chuyển đổi cho phép bạn kết hợp các hiệu ứng ảnh động trong một bộ chuyển đổi chứa một nhóm các chuyển đổi riêng lẻ được tích hợp sẵn hoặc tuỳ chỉnh.

Để xác định một tập hợp chuyển đổi từ một tập hợp các hiệu ứng chuyển đổi trong XML, hãy tạo một tệp tài nguyên trong thư mục res/transitions/ và liệt kê các hiệu ứng chuyển đổi trong phần tử TransitionSet. Ví dụ: đoạn mã sau đây cho biết cách chỉ định một tập hợp chuyển đổi có cùng hành vi với lớp AutoTransition:

<transitionSet xmlns:android="http://schemas.android.com/apk/res/android"
    android:transitionOrdering="sequential">
    <fade android:fadingMode="fade_out" />
    <changeBounds />
    <fade android:fadingMode="fade_in" />
</transitionSet>

Để tăng bộ chuyển đổi thành một đối tượng TransitionSet trong mã của bạn, hãy gọi hàm TransitionInflater.from() trong hoạt động của bạn. Lớp TransitionSet mở rộng từ lớp Transition, vì vậy, bạn có thể sử dụng lớp này với một trình quản lý hiệu ứng chuyển đổi giống như mọi thực thể Transition khác.

Áp dụng hiệu ứng chuyển cảnh mà không có cảnh

Thay đổi hệ phân cấp khung hiển thị không phải là cách duy nhất để sửa đổi giao diện người dùng. Bạn cũng có thể thực hiện các thay đổi bằng cách thêm, sửa đổi và xoá các khung hiển thị con trong hệ phân cấp hiện tại.

Ví dụ: bạn có thể triển khai một hoạt động tương tác tìm kiếm bằng một bố cục duy nhất. Bắt đầu với bố cục có trường nhập nội dung tìm kiếm và biểu tượng tìm kiếm. Để thay đổi giao diện người dùng nhằm hiện kết quả, hãy xoá nút tìm kiếm khi người dùng nhấn vào nút đó bằng cách gọi hàm ViewGroup.removeView() và thêm kết quả tìm kiếm bằng cách gọi hàm ViewGroup.addView().

Bạn có thể sử dụng phương pháp này nếu có 2 hệ thống phân cấp gần như giống hệt nhau. Thay vì tạo và duy trì hai tệp bố cục riêng biệt cho một điểm khác biệt nhỏ trong giao diện người dùng, bạn có thể có một tệp bố cục chứa một hệ phân cấp view mà bạn sửa đổi trong mã.

Nếu thực hiện thay đổi trong hệ thống phân cấp khung hiển thị hiện tại theo cách này, bạn không cần tạo một cảnh. Thay vào đó, bạn có thể tạo và áp dụng hiệu ứng chuyển đổi giữa hai trạng thái của một hệ phân cấp khung hiển thị bằng cách sử dụng hiệu ứng chuyển đổi bị trì hoãn. Tính năng này của khung chuyển đổi bắt đầu bằng trạng thái hiện tại của hệ phân cấp view, ghi lại những thay đổi mà bạn thực hiện đối với các khung hiển thị của hệ phân cấp đó và áp dụng một hiệu ứng chuyển đổi để tạo ảnh động cho những thay đổi đó khi hệ thống vẽ lại giao diện người dùng.

Để tạo hiệu ứng chuyển đổi có độ trễ trong một hệ phân cấp khung hiển thị duy nhất, hãy làm theo các bước sau:

  1. Khi sự kiện kích hoạt hiệu ứng chuyển đổi xảy ra, hãy gọi hàm TransitionManager.beginDelayedTransition(), cung cấp khung hiển thị gốc của tất cả khung hiển thị mà bạn muốn thay đổi và hiệu ứng chuyển đổi cần sử dụng. Khung này lưu trữ trạng thái hiện tại của các khung hiển thị con và giá trị thuộc tính của chúng.
  2. Thay đổi khung hiển thị con theo yêu cầu của trường hợp sử dụng. Khung này ghi lại những thay đổi mà bạn thực hiện đối với các khung hiển thị con và thuộc tính của chúng.
  3. Khi hệ thống vẽ lại giao diện người dùng theo các thay đổi của bạn, khung sẽ tạo hiệu ứng cho các thay đổi giữa trạng thái ban đầu và trạng thái mới.

Ví dụ sau đây minh hoạ cách tạo hiệu ứng cho việc thêm một khung hiển thị văn bản vào một hệ phân cấp khung hiển thị bằng cách sử dụng hiệu ứng chuyển đổi có độ trễ. Đoạn mã đầu tiên cho thấy tệp định nghĩa bố cục:

res/layout/activity_main.xml

<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:id="@+id/mainLayout"
    android:layout_width="match_parent"
    android:layout_height="match_parent" >
    <EditText
        android:id="@+id/inputText"
        android:layout_alignParentLeft="true"
        android:layout_alignParentTop="true"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        app:layout_constraintTop_toTopOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintEnd_toEndOf="parent" />
    ...
</androidx.constraintlayout.widget.ConstraintLayout>

Đoạn mã tiếp theo cho thấy mã minh hoạ việc bổ sung view văn bản:

MainActivity

Kotlin

setContentView(R.layout.activity_main)
val labelText = TextView(this).apply {
    text = "Label"
    id = R.id.text
}
val rootView: ViewGroup = findViewById(R.id.mainLayout)
val mFade: Fade = Fade(Fade.IN)
TransitionManager.beginDelayedTransition(rootView, mFade)
rootView.addView(labelText)

Java

private TextView labelText;
private Fade mFade;
private ViewGroup rootView;
...
// Load the layout.
setContentView(R.layout.activity_main);
...
// Create a new TextView and set some View properties.
labelText = new TextView(this);
labelText.setText("Label");
labelText.setId(R.id.text);

// Get the root view and create a transition.
rootView = (ViewGroup) findViewById(R.id.mainLayout);
mFade = new Fade(Fade.IN);

// Start recording changes to the view hierarchy.
TransitionManager.beginDelayedTransition(rootView, mFade);

// Add the new TextView to the view hierarchy.
rootView.addView(labelText);

// When the system redraws the screen to show this update,
// the framework animates the addition as a fade in.

Xác định các lệnh gọi lại vòng đời chuyển đổi

Vòng đời chuyển đổi tương tự như vòng đời hoạt động. Nó biểu thị các trạng thái chuyển đổi mà khung theo dõi trong khoảng thời gian giữa một lệnh gọi đến hàm TransitionManager.go() và khi hoàn tất ảnh động. Ở các trạng thái quan trọng trong vòng đời, khung sẽ gọi các lệnh gọi lại do giao diện TransitionListener xác định.

Các lệnh gọi lại vòng đời chuyển đổi rất hữu ích, chẳng hạn như để sao chép giá trị thuộc tính khung hiển thị từ hệ phân cấp khung hiển thị bắt đầu sang hệ phân cấp khung hiển thị kết thúc trong quá trình thay đổi cảnh. Bạn không thể chỉ cần sao chép giá trị từ khung hiển thị bắt đầu sang khung hiển thị trong hệ phân cấp khung hiển thị kết thúc, vì hệ phân cấp khung hiển thị kết thúc sẽ không được mở rộng cho đến khi quá trình chuyển đổi hoàn tất. Thay vào đó, bạn cần lưu trữ giá trị trong một biến, rồi sao chép giá trị đó vào hệ phân cấp khung hiển thị kết thúc khi khung hoàn tất quá trình chuyển đổi. Để nhận được thông báo khi quá trình chuyển đổi hoàn tất, hãy triển khai hàm TransitionListener.onTransitionEnd() trong hoạt động của bạn.

Để biết thêm thông tin, hãy xem tài liệu tham khảo API cho lớp TransitionListener.

Các điểm hạn chế

Phần này liệt kê một số hạn chế đã biết của khung chuyển đổi:

  • Ảnh động được áp dụng cho SurfaceView có thể không xuất hiện chính xác. Các thực thể SurfaceView được cập nhật từ một luồng không phải luồng giao diện người dùng, do đó, các bản cập nhật có thể không đồng bộ hoá với ảnh động của các khung hiển thị khác.
  • Một số loại hiệu ứng chuyển đổi cụ thể có thể không tạo ra hiệu ứng hoạt ảnh mong muốn khi được áp dụng cho một TextureView.
  • Các lớp mở rộng AdapterView, chẳng hạn như ListView, quản lý các khung hiển thị con theo cách không tương thích với khung chuyển đổi. Nếu bạn cố gắng tạo ảnh động cho một khung hiển thị dựa trên AdapterView, thì màn hình thiết bị có thể ngừng phản hồi.
  • Nếu bạn cố gắng đổi kích thước một TextView bằng một ảnh động, thì văn bản sẽ xuất hiện ở một vị trí mới trước khi đối tượng được đổi kích thước hoàn toàn. Để tránh vấn đề này, đừng tạo hiệu ứng cho việc đổi kích thước các khung hiển thị có chứa văn bản.