Tạo hiệu ứng Chuyển đổi đẹp mắt bằng Material Motion dành cho Android

1. Giới thiệu

Material Design là một hệ thống để xây dựng các sản phẩm kỹ thuật số nổi bật và đẹp mắt. Bằng cách kết hợp phong cách, thương hiệu, hoạt động tương tác và chuyển động theo một bộ nguyên tắc và thành phần nhất quán, các nhóm sản phẩm có thể nhận ra tiềm năng thiết kế lớn nhất của mình.

logo_components_color_2x_web_96dp.png

Thành phần Material (MDC) giúp nhà phát triển triển khai Material Design. Được tạo bởi một nhóm kỹ sư và nhà thiết kế trải nghiệm người dùng tại Google, MDC có hàng chục thành phần giao diện người dùng đẹp mắt và có chức năng, đồng thời có sẵn cho Android, iOS, web và Flutter.material.io/develop

Hệ thống chuyển động của Material cho Android là gì?

Hệ thống chuyển động của Material cho Android là một tập hợp các mẫu chuyển đổi trong thư viện MDC-Android, có thể giúp người dùng hiểu và điều hướng một ứng dụng, như mô tả trong nguyên tắc Material Design.

Sau đây là 4 mẫu chuyển đổi chính của Material:

  • Biến đổi vùng chứa: chuyển đổi giữa các phần tử giao diện người dùng có vùng chứa; tạo mối liên kết dễ thấy giữa 2 phần tử giao diện người dùng riêng biệt bằng cách biến đổi một phần tử thành phần tử khác một cách liền mạch.
  • Trục chung: chuyển đổi giữa các phần tử giao diện người dùng có mối quan hệ về không gian hoặc điều hướng; sử dụng một phép biến đổi chung trên trục x, y hoặc z để củng cố mối quan hệ giữa các phần tử.
  • Chuyển mờ: chuyển đổi giữa các phần tử trên giao diện người dùng không có mối quan hệ chặt chẽ với nhau; sử dụng hiệu ứng mờ dần rồi mờ dần theo trình tự, với tỷ lệ của phần tử đến.
  • Làm mờ: dùng cho các thành phần giao diện người dùng đi vào hoặc thoát ra trong phạm vi màn hình.

Thư viện MDC-Android cung cấp các lớp chuyển đổi cho những mẫu này, được xây dựng trên cả Thư viện Chuyển đổi AndroidX (androidx.transition) và Khung chuyển đổi Android (android.transition):

AndroidX

  • Có trong gói com.google.android.material.transition
  • Hỗ trợ API cấp 14 trở lên
  • Hỗ trợ Mảnh và Khung hiển thị, nhưng không hỗ trợ Hoạt động hoặc Cửa sổ
  • Chứa các bản sửa lỗi được chuyển ngược và hành vi nhất quán trên các Cấp độ API

Framework

  • Có trong gói com.google.android.material.transition.platform
  • Hỗ trợ API cấp 21 trở lên
  • Hỗ trợ các Mảnh, Khung hiển thị, Hoạt động và Cửa sổ
  • Các bản sửa lỗi không được chuyển ngược lại và có thể có hành vi khác nhau trên các Cấp độ API

Trong lớp học lập trình này, bạn sẽ sử dụng các hiệu ứng chuyển đổi Material được xây dựng trên nền tảng thư viện AndroidX, tức là bạn sẽ chủ yếu tập trung vào các Mảnh và Khung hiển thị.

Sản phẩm bạn sẽ tạo ra

Lớp học lập trình này sẽ hướng dẫn bạn cách tạo một số hiệu ứng chuyển đổi trong một ứng dụng email Android mẫu có tên là Reply bằng Kotlin, để minh hoạ cách bạn có thể sử dụng hiệu ứng chuyển đổi từ thư viện MDC-Android để tuỳ chỉnh giao diện của ứng dụng.

Mã khởi đầu cho ứng dụng Reply sẽ được cung cấp và bạn sẽ kết hợp các hiệu ứng chuyển đổi Material sau đây vào ứng dụng. Bạn có thể xem các hiệu ứng này trong GIF của lớp học lập trình đã hoàn tất bên dưới:

  • Hiệu ứng chuyển đổi Biến đổi vùng chứa từ danh sách email sang trang chi tiết email
  • Hiệu ứng chuyển đổi Biến đổi vùng chứa từ FAB sang trang soạn email
  • Hiệu ứng chuyển đổi Trục Z chung từ biểu tượng tìm kiếm sang trang chế độ xem tìm kiếm
  • Hiệu ứng chuyển đổi Mờ dần giữa các trang hộp thư
  • Hiệu ứng chuyển đổi Container Transform từ khối địa chỉ email sang chế độ xem thẻ

Miền của iframe được yêu cầu (youtu.be) chưa được đưa vào danh sách cho phép.

Bạn cần có

  • Có kiến thức cơ bản về phát triển Android và Kotlin
  • Android Studio (tải xuống tại đây nếu bạn chưa có)
  • Một trình mô phỏng hoặc thiết bị Android (có trong Android Studio)
  • Mã mẫu (xem bước tiếp theo)

Bạn đánh giá thế nào về kinh nghiệm của mình trong việc tạo ứng dụng Android?

Người mới bắt đầu Trung cấp Thành thạo

2. Thiết lập môi trường phát triển

Khởi động Android Studio

Khi bạn mở Android Studio, ứng dụng này sẽ hiển thị một cửa sổ có tiêu đề "Chào mừng bạn đến với Android Studio". Tuy nhiên, nếu đây là lần đầu tiên bạn chạy Android Studio, hãy thực hiện các bước trong Android Studio Setup Wizard (Trình hướng dẫn thiết lập Android Studio) với các giá trị mặc định. Bước này có thể mất vài phút để tải xuống và cài đặt các tệp cần thiết, vì vậy, bạn có thể để quá trình này chạy ở chế độ nền trong khi thực hiện phần tiếp theo.

Cách 1: Sao chép ứng dụng khởi đầu của lớp học lập trình trên GitHub

Để nhân bản lớp học lập trình này từ GitHub, hãy chạy các lệnh sau:

git clone https://github.com/material-components/material-components-android-motion-codelab.git
cd material-components-android-motion-codelab

Cách 2: Tải xuống tệp ZIP của ứng dụng lớp học lập trình dành cho người mới bắt đầu

Ứng dụng khởi đầu nằm trong thư mục material-components-android-motion-codelab-develop.

Tải mã khởi đầu trong Android Studio

  1. Sau khi trình hướng dẫn thiết lập hoàn tất và cửa sổ Welcome to Android Studio (Chào mừng bạn đến với Android Studio) xuất hiện, hãy nhấp vào Open an existing Android Studio project (Mở một dự án hiện có trong Android Studio).

e3f200327a67a53.png

  1. Chuyển đến thư mục mà bạn đã cài đặt mã mẫu và chọn thư mục mẫu để mở dự án.
  2. Chờ một lát để Android Studio tạo và đồng bộ hoá dự án, như được thể hiện bằng các chỉ báo hoạt động ở cuối cửa sổ Android Studio.
  1. Tại thời điểm này, Android Studio có thể phát sinh một số lỗi bản dựng vì bạn thiếu SDK Android hoặc các công cụ bản dựng, chẳng hạn như lỗi được minh hoạ bên dưới. Hãy làm theo hướng dẫn trong Android Studio để cài đặt/cập nhật các thành phần này và đồng bộ hoá dự án của bạn. Nếu bạn vẫn gặp vấn đề, hãy làm theo hướng dẫn về cách cập nhật các công cụ bằng Trình quản lý SDK.

6e026ae171f5b1eb.png

Xác minh các phần phụ thuộc của dự án

Dự án cần có một phần phụ thuộc trên thư viện MDC-Android. Mã mẫu mà bạn đã tải xuống có thể đã liệt kê phần phụ thuộc này, nhưng hãy xem cấu hình để đảm bảo.

Chuyển đến tệp build.gradle của mô-đun app và đảm bảo rằng khối dependencies có một phần phụ thuộc vào MDC-Android:

implementation 'com.google.android.material:material:1.2.0'

Chạy ứng dụng khởi đầu

  1. Đảm bảo rằng cấu hình bản dựng ở bên trái lựa chọn thiết bị là app.
  2. Nhấn vào nút Chạy / Phát màu xanh lục để tạo và chạy ứng dụng.

24218d0a6ae25803.png

  1. Trong cửa sổ Select Deployment Target (Chọn mục tiêu triển khai), nếu bạn đã có một thiết bị Android trong danh sách các thiết bị có sẵn, hãy chuyển đến Bước 8. Nếu không, hãy nhấp vào Create New Virtual Device (Tạo thiết bị ảo mới).
  2. Trong màn hình Select Hardware (Chọn phần cứng), hãy chọn một thiết bị điện thoại, chẳng hạn như Pixel 3, rồi nhấp vào Next (Tiếp theo).
  3. Trong màn hình System Image (Hình ảnh hệ thống), hãy chọn một phiên bản Android gần đây, tốt nhất là cấp độ API cao nhất. Nếu chưa cài đặt, hãy nhấp vào đường liên kết Tải xuống xuất hiện rồi hoàn tất quá trình tải xuống.
  4. Nhấp vào Tiếp theo.
  5. Trên màn hình Android Virtual Device (AVD) (Thiết bị Android ảo), hãy giữ nguyên các chế độ cài đặt rồi nhấp vào Finish (Hoàn tất).
  6. Chọn một thiết bị Android trong hộp thoại mục tiêu triển khai.
  7. Nhấp vào Ok.
  8. Android Studio sẽ tạo bản dựng ứng dụng, triển khai ứng dụng và tự động mở ứng dụng đó trên thiết bị mục tiêu.

Thành công! Mã khởi đầu cho trang chủ của Reply sẽ chạy trong trình mô phỏng. Bạn sẽ thấy hộp thư đến chứa danh sách email.

cc73eb0d0f779035.png

Không bắt buộc: Giảm tốc độ ảnh động trên thiết bị

Vì lớp học lập trình này liên quan đến các hiệu ứng chuyển đổi nhanh nhưng mượt mà, nên bạn có thể làm chậm các hiệu ứng động của thiết bị để quan sát một số chi tiết tinh tế hơn của các hiệu ứng chuyển đổi khi triển khai. Bạn có thể thực hiện việc này bằng các lệnh shell adb hoặc Ô Cài đặt nhanh. Xin lưu ý rằng những phương thức làm chậm ảnh động trên thiết bị này cũng sẽ ảnh hưởng đến ảnh động trên thiết bị bên ngoài ứng dụng Reply.

Phương thức 1: Lệnh ADB Shell

Để giảm tốc độ ảnh động của thiết bị xuống 10 lần, bạn có thể chạy các lệnh sau qua dòng lệnh:

adb shell settings put global window_animation_scale 10
adb shell settings put global transition_animation_scale 10
adb shell settings put global animator_duration_scale 10

Để đặt lại tốc độ ảnh động của thiết bị về mức bình thường, hãy chạy các lệnh sau:

adb shell settings put global window_animation_scale 1
adb shell settings put global transition_animation_scale 1
adb shell settings put global animator_duration_scale 1

Cách 2: Ô Cài đặt nhanh

Ngoài ra, để thiết lập Ô cài đặt nhanh, trước tiên, hãy bật phần Cài đặt cho nhà phát triển trên thiết bị nếu bạn chưa bật:

  1. Mở ứng dụng "Cài đặt" trên thiết bị
  2. Di chuyển xuống dưới cùng rồi nhấp vào "Giới thiệu về thiết bị mô phỏng"
  3. Di chuyển xuống dưới cùng rồi nhấp nhanh vào "Số bản dựng" cho đến khi Chế độ cài đặt cho nhà phát triển được bật

Tiếp theo, hãy làm như sau (vẫn trong ứng dụng "Cài đặt" của thiết bị) để bật Ô cài đặt nhanh:

  1. Nhấp vào biểu tượng tìm kiếm hoặc thanh tìm kiếm ở đầu màn hình
  2. Nhập "tiles" vào trường tìm kiếm
  3. Nhấp vào hàng "Ô cài đặt nhanh dành cho nhà phát triển"
  4. Nhấp vào nút chuyển "Tỷ lệ hình động của cửa sổ"

Cuối cùng, trong suốt lớp học lập trình, hãy kéo ngăn thông báo hệ thống xuống từ đầu màn hình và dùng biểu tượng c7e3f98200023f6a.png để chuyển đổi giữa hoạt ảnh tốc độ chậm và tốc độ bình thường.

3. Làm quen với mã ứng dụng mẫu

Hãy cùng xem mã này. Chúng tôi đã cung cấp một ứng dụng sử dụng thư viện thành phần Điều hướng của Jetpack để di chuyển giữa một số Mảnh khác nhau, tất cả đều nằm trong một Hoạt động duy nhất, MainActivity:

  • HomeFragment: hiển thị danh sách email
  • EmailFragment: hiển thị một email đầy đủ
  • ComposeFragment: cho phép soạn email mới
  • SearchFragment: hiển thị một khung hiển thị tìm kiếm

Trước tiên, để tìm hiểu cách thiết lập biểu đồ điều hướng của ứng dụng, hãy mở navigation_graph.xml trong thư mục app -> src -> main -> res -> navigation:

<navigation xmlns:android="http://schemas.android.com/apk/res/android"
   xmlns:app="http://schemas.android.com/apk/res-auto"
   android:id="@+id/navigation_graph"
   app:startDestination="@id/homeFragment">

   <fragment
       android:id="@+id/homeFragment"
       android:name="com.materialstudies.reply.ui.home.HomeFragment"
       android:label="HomeFragment">
       <argument...>
       <action
           android:id="@+id/action_homeFragment_to_emailFragment"
           app:destination="@id/emailFragment" />
   </fragment>
   <fragment
       android:id="@+id/emailFragment"
       android:name="com.materialstudies.reply.ui.email.EmailFragment"
       android:label="EmailFragment">
       <argument...>
   </fragment>
   <fragment
       android:id="@+id/composeFragment"
       android:name="com.materialstudies.reply.ui.compose.ComposeFragment"
       android:label="ComposeFragment">
       <argument...>
   </fragment>
   <fragment
       android:id="@+id/searchFragment"
       android:name="com.materialstudies.reply.ui.search.SearchFragment"
       android:label="SearchFragment" />
   <action
       android:id="@+id/action_global_homeFragment"
       app:destination="@+id/homeFragment"
       app:launchSingleTop="true"
       app:popUpTo="@+id/navigation_graph"
       app:popUpToInclusive="true"/>
   <action
       android:id="@+id/action_global_composeFragment"
       app:destination="@+id/composeFragment" />
   <action
       android:id="@+id/action_global_searchFragment"
       app:destination="@+id/searchFragment" />
</navigation>

Lưu ý cách tất cả các fragment nêu trên xuất hiện, với fragment khởi chạy mặc định được đặt thành HomeFragment thông qua app:startDestination="@id/homeFragment". Định nghĩa XML này của biểu đồ đích đến của fragment, cũng như các thao tác, sẽ thông báo cho mã điều hướng Kotlin được tạo mà bạn sẽ gặp phải khi kết nối các hiệu ứng chuyển đổi.

activity_main.xml

Tiếp theo, hãy xem bố cục activity_main.xml trong thư mục app -> src -> main -> res -> layout. Bạn sẽ thấy NavHostFragment được định cấu hình bằng biểu đồ điều hướng ở trên:

<fragment
   android:id="@+id/nav_host_fragment"
   android:name="androidx.navigation.fragment.NavHostFragment"
   android:layout_width="match_parent"
   android:layout_height="match_parent"
   app:defaultNavHost="true"
   app:navGraph="@navigation/navigation_graph"/>

NavHostFragment này sẽ lấp đầy màn hình và xử lý mọi thay đổi về hoạt động điều hướng của fragment ở chế độ toàn màn hình trong ứng dụng. BottomAppBarFloatingActionButton được cố định của nó (cũng ở activity_main.xml) sẽ được bố trí ở trên cùng của fragment hiện tại do NavHostFragment hiển thị. Do đó, BottomAppBarFloatingActionButton sẽ xuất hiện hoặc bị ẩn tuỳ thuộc vào đích đến của fragment theo mã ứng dụng mẫu được cung cấp.

Ngoài ra, BottomNavDrawerFragment trong activity_main.xml là một ngăn dưới cùng chứa trình đơn để di chuyển giữa các hộp thư email khác nhau, được hiển thị có điều kiện thông qua nút biểu trưng BottomAppBar Trả lời.

MainActivity.kt

Cuối cùng, để xem ví dụ về thao tác điều hướng đang được sử dụng, hãy mở MainActivity.kt trong thư mục app -> src -> main -> java -> com.materialstudies.reply.ui. Xác định vị trí hàm navigateToSearch(). Hàm này sẽ có dạng như sau:

private fun navigateToSearch() {
   val directions = SearchFragmentDirections.actionGlobalSearchFragment()
   findNavController(R.id.nav_host_fragment).navigate(directions)
}

Ví dụ này cho thấy cách bạn có thể chuyển đến trang xem tìm kiếm mà không có hiệu ứng chuyển đổi tuỳ chỉnh. Trong lớp học lập trình này, bạn sẽ tìm hiểu sâu về MainActivity và 4 fragment chính của ứng dụng Reply để thiết lập các hiệu ứng chuyển đổi Material hoạt động song song với nhiều thao tác điều hướng trong ứng dụng.

Giờ khi bạn đã quen thuộc với mã khởi đầu, hãy triển khai hiệu ứng chuyển đổi đầu tiên.

4. Thêm hiệu ứng chuyển đổi Biến đổi vùng chứa từ danh sách email sang trang thông tin chi tiết email

Để bắt đầu, bạn sẽ thêm một hiệu ứng chuyển đổi khi nhấp vào email. Đối với thay đổi điều hướng này, mẫu biến đổi vùng chứa rất phù hợp vì được thiết kế cho quá trình chuyển đổi giữa các phần tử trên giao diện người dùng có vùng chứa. Mẫu này tạo ra sự kết nối dễ thấy giữa 2 phần tử trên giao diện người dùng.

Trước khi thêm bất kỳ mã nào, hãy thử chạy ứng dụng Reply và nhấp vào một email. Đoạn video này chỉ cần có một cảnh cắt đơn giản, tức là màn hình được thay thế mà không có hiệu ứng chuyển cảnh:

f0e8a92eb2216bce.gif

Bắt đầu bằng cách thêm thuộc tính transitionName vào MaterialCardView trong email_item_layout.xml như minh hoạ trong đoạn mã sau:

email_item_layout.xml

android:transitionName="@{@string/email_card_transition_name(email.id)}"

Tên chuyển đổi nhận một tài nguyên chuỗi có tham số. Bạn cần sử dụng mã nhận dạng của từng email để đảm bảo mỗi transitionName trong EmailFragment của chúng tôi đều là duy nhất.

Giờ đây, bạn đã đặt tên chuyển đổi cho mục danh sách email. Hãy làm tương tự trong bố cục chi tiết email. Trong fragment_email.xml, hãy đặt transitionName của MaterialCardView thành tài nguyên chuỗi sau:

fragment_email.xml

android:transitionName="@string/email_card_detail_transition_name"

Trong HomeFragment.kt, hãy thay thế mã trong onEmailClicked bằng đoạn mã bên dưới để tạo mối liên kết từ khung hiển thị bắt đầu (mục trong danh sách email) và khung hiển thị kết thúc (màn hình chi tiết email):

HomeFragment.kt

val emailCardDetailTransitionName = getString(R.string.email_card_detail_transition_name)
val extras = FragmentNavigatorExtras(cardView to emailCardDetailTransitionName)
val directions = HomeFragmentDirections.actionHomeFragmentToEmailFragment(email.id)
findNavController().navigate(directions, extras)

Sau khi định cấu hình xong các thành phần cơ bản, bạn có thể tạo một hiệu ứng biến đổi vùng chứa. Trong phương thức EmailFragment onCreate, hãy đặt sharedElementEnterTransition thành một thực thể mới của MaterialContainerTransform (nhập phiên bản com.google.android.material.transition thay vì phiên bản com.google.android.material.transition.platform) bằng cách thêm đoạn mã sau:

EmailFragment.kt

sharedElementEnterTransition = MaterialContainerTransform().apply {
   drawingViewId = R.id.nav_host_fragment
   duration = resources.getInteger(R.integer.reply_motion_duration_large).toLong()
   scrimColor = Color.TRANSPARENT
   setAllContainerColors(requireContext().themeColor(R.attr.colorSurface))
}

Bây giờ, hãy thử chạy lại ứng dụng.

ed62cedec31da268.gif

Mọi thứ đang bắt đầu trở nên tuyệt vời! Khi bạn nhấp vào một email trong danh sách email, một hiệu ứng biến đổi vùng chứa sẽ mở rộng mục trong danh sách thành một trang thông tin chi tiết toàn màn hình. Tuy nhiên, hãy lưu ý rằng thao tác nhấn nút quay lại sẽ không thu gọn email trở lại danh sách. Ngoài ra, danh sách email sẽ biến mất ngay khi bắt đầu chuyển đổi, cho thấy nền cửa sổ màu xám. Vì vậy, chúng ta vẫn chưa hoàn thành.

Để khắc phục hiệu ứng chuyển đổi trả về, hãy thêm 2 dòng sau vào phương thức onViewCreated trong HomeFragment.kt:

HomeFragment.kt

postponeEnterTransition()
view.doOnPreDraw { startPostponedEnterTransition() }

Hãy thử chạy lại ứng dụng. Khi bạn nhấn nút quay lại sau khi mở một email, email đó sẽ thu gọn lại thành danh sách. Tuyệt vời! Hãy tiếp tục cải thiện ảnh động.

Vấn đề danh sách email biến mất là do khi điều hướng đến một Mảnh mới bằng Thành phần điều hướng, Mảnh hiện tại sẽ bị xoá ngay lập tức và được thay thế bằng Mảnh mới, sắp tới của chúng ta. Để giữ cho danh sách email hiển thị ngay cả sau khi được thay thế, bạn có thể thêm hiệu ứng chuyển đổi thoát vào HomeFragment.

Thêm đoạn mã bên dưới vào phương thức HomeFragment onEmailClicked để danh sách email có thể mở rộng một cách tinh tế khi thoát và thu nhỏ lại khi truy cập lại:

HomeFragment.kt

exitTransition = MaterialElevationScale(false).apply {
   duration = resources.getInteger(R.integer.reply_motion_duration_large).toLong()
}
reenterTransition = MaterialElevationScale(true).apply {
   duration = resources.getInteger(R.integer.reply_motion_duration_large).toLong()
}

Tiếp theo, để đảm bảo rằng hiệu ứng chuyển đổi MaterialElevationScale được áp dụng cho toàn bộ màn hình chính, thay vì cho từng khung hiển thị riêng lẻ trong hệ phân cấp, hãy đánh dấu RecyclerView trong fragment_home.xml là một nhóm hiệu ứng chuyển đổi.

fragment_home.xml

android:transitionGroup="true"

Ở giai đoạn này, bạn sẽ có một quy tắc chuyển đổi vùng chứa hoạt động đầy đủ. Khi bạn nhấp vào một email, mục đó trong danh sách sẽ mở rộng thành một màn hình chi tiết trong khi danh sách email sẽ thu hẹp lại. Khi bạn nhấn nút quay lại, màn hình chi tiết email sẽ thu gọn thành một mục trong danh sách trong khi mở rộng trong danh sách email.

9df2b39d5a150418.gif

5. Thêm hiệu ứng chuyển đổi Biến đổi vùng chứa từ FAB vào trang soạn email

Hãy tiếp tục với hiệu ứng biến đổi vùng chứa và thêm một hiệu ứng chuyển đổi từ Nút hành động nổi sang ComposeFragment, mở rộng FAB thành một email mới mà người dùng sẽ viết. Trước tiên, hãy chạy lại ứng dụng và nhấp vào FAB để thấy rằng không có hiệu ứng chuyển đổi khi khởi chạy màn hình soạn email.

d242c9708abd382c.gif

Mặc dù chúng ta sử dụng cùng một lớp hiệu ứng chuyển đổi, nhưng cách chúng ta định cấu hình thực thể này sẽ khác vì FAB nằm trong MainActivityComposeFragment được đặt bên trong vùng chứa máy chủ điều hướng MainActivity.

Trong ComposeFragment.kt, hãy thêm đoạn mã sau vào phương thức onViewCreated, nhớ nhập phiên bản androidx.transition của Slide.

ComposeFragment.kt

enterTransition = MaterialContainerTransform().apply {
   startView = requireActivity().findViewById(R.id.fab)
   endView = emailCardView
   duration = resources.getInteger(R.integer.reply_motion_duration_large).toLong()
   scrimColor = Color.TRANSPARENT
   containerColor = requireContext().themeColor(R.attr.colorSurface)
   startContainerColor = requireContext().themeColor(R.attr.colorSecondary)
   endContainerColor = requireContext().themeColor(R.attr.colorSurface)
}
returnTransition = Slide().apply {
   duration = resources.getInteger(R.integer.reply_motion_duration_medium).toLong()
   addTarget(R.id.email_card_view)
}

Ngoài các tham số dùng để định cấu hình biến đổi vùng chứa trước đó, startViewendView đang được đặt theo cách thủ công tại đây. Thay vì dùng thuộc tính transitionName để cho hệ thống Chuyển đổi của Android biết những khung hiển thị nào cần được chuyển đổi, bạn có thể chỉ định các khung hiển thị này theo cách thủ công khi cần.

Bây giờ, hãy chạy lại ứng dụng. Bạn sẽ thấy FAB biến đổi thành màn hình Compose (xem ảnh GIF ở cuối bước này).

Tương tự như bước trước, bạn cần thêm một hiệu ứng chuyển cảnh vào HomeFragment để ngăn hiệu ứng này biến mất sau khi bị xoá và thay thế bằng ComposeFragment.

Sao chép đoạn mã bên dưới vào phương thức navigateToCompose trong MainActivity trước khi gọi NavController navigate.

MainActivity.kt

currentNavigationFragment?.apply {
   exitTransition = MaterialElevationScale(false).apply {
       duration = resources.getInteger(R.integer.reply_motion_duration_large).toLong()
   }
   reenterTransition = MaterialElevationScale(true).apply {
       duration = resources.getInteger(R.integer.reply_motion_duration_large).toLong()
   }
}

Vậy là xong bước này! Bạn sẽ có một hiệu ứng chuyển đổi từ FAB sang màn hình Compose trông như sau:

81b68391ac4b0a9.gif

6. Thêm hiệu ứng chuyển đổi Trục Z dùng chung từ biểu tượng tìm kiếm sang trang chế độ xem tìm kiếm

Trong bước này, chúng ta sẽ thêm hiệu ứng chuyển đổi từ biểu tượng tìm kiếm sang khung hiển thị tìm kiếm toàn màn hình. Vì không có vùng chứa cố định nào liên quan đến thay đổi điều hướng này, nên chúng ta có thể sử dụng hiệu ứng chuyển đổi Trục Z chung để củng cố mối quan hệ không gian giữa hai màn hình và cho biết việc di chuyển lên một cấp trong hệ phân cấp của ứng dụng.

Trước khi thêm bất kỳ mã nào khác, hãy thử chạy ứng dụng và nhấn vào biểu tượng tìm kiếm ở góc dưới cùng bên phải của màn hình. Thao tác này sẽ mở màn hình khung hiển thị tìm kiếm mà không có hiệu ứng chuyển đổi.

499e1a677b4216bb.gif

Để bắt đầu, hãy tìm phương thức navigateToSearch trong MainActivity rồi thêm đoạn mã sau trước lệnh gọi phương thức NavController navigate để thiết lập các hiệu ứng chuyển đổi MaterialSharedAxis theo trục Z khi thoát và nhập lại của fragment hiện tại.

MainActivity.kt

currentNavigationFragment?.apply {
   exitTransition = MaterialSharedAxis(MaterialSharedAxis.Z, true).apply {
       duration = resources.getInteger(R.integer.reply_motion_duration_large).toLong()
   }
   reenterTransition = MaterialSharedAxis(MaterialSharedAxis.Z, false).apply {
       duration = resources.getInteger(R.integer.reply_motion_duration_large).toLong()
   }
}

Tiếp theo, hãy thêm đoạn mã sau vào phương thức onCreate trong SearchFragment. Đoạn mã này sẽ định cấu hình các hiệu ứng chuyển đổi MaterialSharedAxis khi vào và khi quay lại.

SearchFragment.kt

enterTransition = MaterialSharedAxis(MaterialSharedAxis.Z, true).apply {
   duration = resources.getInteger(R.integer.reply_motion_duration_large).toLong()
}
returnTransition = MaterialSharedAxis(MaterialSharedAxis.Z, false).apply {
   duration = resources.getInteger(R.integer.reply_motion_duration_large).toLong()
}

Cuối cùng, để đảm bảo rằng hiệu ứng chuyển đổi MaterialSharedAxis được áp dụng cho toàn bộ màn hình tìm kiếm, thay vì cho từng khung hiển thị riêng lẻ trong hệ phân cấp, hãy đánh dấu LinearLayout trong fragment_search.xml là một nhóm hiệu ứng chuyển đổi.

fragment_search.xml

android:transitionGroup="true"

Vậy là xong! Bây giờ, hãy thử chạy lại ứng dụng rồi nhấn vào biểu tượng tìm kiếm. Màn hình chính và màn hình tìm kiếm sẽ đồng thời mờ dần và thu phóng theo trục Z theo chiều sâu, tạo hiệu ứng liền mạch giữa hai màn hình.

e5c0b0a130e807db.gif

7. Thêm hiệu ứng chuyển đổi Mờ dần giữa các trang hộp thư

Trong bước này, chúng ta sẽ thêm một hiệu ứng chuyển đổi giữa các hộp thư. Vì không muốn nhấn mạnh mối quan hệ về không gian hoặc thứ bậc, nên chúng ta sẽ sử dụng hiệu ứng mờ dần để thực hiện thao tác "hoán đổi" đơn giản giữa các danh sách email.

Trước khi thêm bất kỳ mã nào khác, hãy thử chạy ứng dụng, nhấn vào biểu trưng Reply (Trả lời) trong Thanh ứng dụng dưới cùng và chuyển đổi hộp thư. Danh sách email sẽ thay đổi mà không có hiệu ứng chuyển đổi.

2c874c0a4588e8fb.gif

Để bắt đầu, hãy tìm phương thức navigateToHome trong MainActivity rồi thêm đoạn mã sau trước lệnh gọi phương thức NavController navigate để thiết lập hiệu ứng chuyển đổi MaterialFadeThrough thoát của mảnh hiện tại.

MainActivity.kt

currentNavigationFragment?.apply {
   exitTransition = MaterialFadeThrough().apply {
       duration = resources.getInteger(R.integer.reply_motion_duration_large).toLong()
   }
}

Tiếp theo, hãy mở HomeFragment. Trong onCreate, hãy đặt enterTransition của mảnh thành một thực thể mới của MaterialFadeThrough.

HomeFragment.kt

enterTransition = MaterialFadeThrough().apply {
   duration = resources.getInteger(R.integer.reply_motion_duration_large).toLong()
}

Chạy lại ứng dụng. Khi bạn mở ngăn điều hướng dưới cùng và thay đổi hộp thư, danh sách email hiện tại sẽ mờ dần và thu nhỏ, trong khi danh sách mới sẽ mờ dần và phóng to. Tuyệt vời!

f61dfd58ea7bd3fd.gif

8. Thêm hiệu ứng chuyển đổi Container Transform từ khối địa chỉ email sang chế độ xem thẻ

Ở bước này, bạn sẽ thêm một hiệu ứng chuyển đổi biến một chip thành thẻ bật lên. Ở đây, một hiệu ứng biến đổi vùng chứa được dùng để giúp người dùng biết rằng hành động được thực hiện trong cửa sổ bật lên sẽ ảnh hưởng đến chip mà cửa sổ bật lên bắt nguồn từ đó.

Trước khi thêm bất kỳ mã nào, hãy chạy ứng dụng Reply, nhấp vào một email, nhấp vào FAB "trả lời", rồi thử nhấp vào thẻ liên hệ của người nhận. Khối này sẽ biến mất ngay lập tức và một thẻ có địa chỉ email của người liên hệ đó sẽ xuất hiện mà không có hiệu ứng động.

6200c682da2382d5.gif

Bạn sẽ làm việc trong ComposeFragment cho bước này. Các khối người nhận (hiển thị theo mặc định) và thẻ người nhận (không hiển thị theo mặc định) đã được thêm vào bố cục ComposeFragment. Thẻ này và chip người nhận là hai khung hiển thị mà bạn sẽ tạo một hiệu ứng biến đổi vùng chứa giữa chúng.

Để bắt đầu, hãy mở ComposeFragment và tìm phương thức expandChip. Phương thức này được gọi khi người dùng nhấp vào chip được cung cấp. Thêm đoạn mã sau vào phía trên các dòng mã hoán đổi khả năng hiển thị recipientCardViewchip. Đoạn mã này sẽ kích hoạt hiệu ứng biến đổi vùng chứa đã đăng ký thông qua beginDelayedTransition.

ComposeFragment.kt

val transform = MaterialContainerTransform().apply {
   startView = chip
   endView = binding.recipientCardView
   scrimColor = Color.TRANSPARENT
   endElevation = requireContext().resources.getDimension(
       R.dimen.email_recipient_card_popup_elevation_compat
   )
   addTarget(binding.recipientCardView)
}

TransitionManager.beginDelayedTransition(binding.composeConstraintLayout, transform)

Nếu bạn chạy ứng dụng ngay lúc này, khối này sẽ chuyển đổi thành thẻ địa chỉ email của người nhận. Tiếp theo, hãy định cấu hình hiệu ứng chuyển đổi trả về để thu gọn thẻ về lại thành chip.

Trong phương thức collapseChip trong ComposeFragment, hãy thêm đoạn mã dưới đây để thu gọn thẻ về lại thành chip.

ComposeFragment.kt

val transform = MaterialContainerTransform().apply {
   startView = binding.recipientCardView
   endView = chip
   scrimColor = Color.TRANSPARENT
   startElevation = requireContext().resources.getDimension(
       R.dimen.email_recipient_card_popup_elevation_compat
   )
   addTarget(chip)
}

TransitionManager.beginDelayedTransition(binding.composeConstraintLayout, transform)

Chạy lại ứng dụng. Khi nhấp vào khối thông tin, khối thông tin sẽ mở rộng thành thẻ, còn khi nhấp vào thẻ, thẻ sẽ thu gọn lại thành khối thông tin. Tuyệt vời!

e823b28e2890e05d.gif

9. Đã xong

Với chưa đến 100 dòng mã Kotlin và một số mã đánh dấu XML cơ bản, thư viện MDC-Android đã giúp bạn tạo các hiệu ứng chuyển đổi đẹp mắt trong một ứng dụng hiện có tuân thủ nguyên tắc của Material Design, đồng thời có giao diện và hành vi nhất quán trên tất cả các thiết bị Android.

454a47ba96017a25.gif

Các bước tiếp theo

Để biết thêm thông tin về hệ thống chuyển động của Material, hãy nhớ xem quy cách và toàn bộ tài liệu dành cho nhà phát triển, đồng thời thử thêm một số hiệu ứng chuyển đổi Material vào ứng dụng của bạn!

Cảm ơn bạn đã dùng thử chuyển động theo ngôn ngữ thiết kế Material. Chúng tôi hy vọng bạn thích lớp học lập trình này!

Tôi đã hoàn thành lớp học lập trình này trong một khoảng thời gian hợp lý và không tốn nhiều công sức

Hoàn toàn đồng ý Đồng ý Trung lập Không đồng ý Hoàn toàn không đồng ý

Tôi muốn tiếp tục sử dụng hệ thống chuyển động của Material trong tương lai

Hoàn toàn đồng ý Đồng ý Không đồng ý cũng không phản đối Không đồng ý Hoàn toàn không đồng ý