Android Kotlin Fundamentals 07.2: DiffUtil và liên kết dữ liệu với RecyclerView

Lớp học lập trình này nằm trong khóa học về Khái niệm cơ bản về Android Kotlin. Bạn sẽ nhận được nhiều giá trị nhất từ khóa học này nếu bạn làm việc qua các lớp học lập trình theo trình tự. Tất cả các lớp học lập trình trong khóa học đều có trên trang đích của các lớp học lập trình cơ bản về Android Kotlin.

Giới thiệu

Trong lớp học lập trình trước đây, bạn đã cập nhật ứng dụng TrackMySleepChất lượng để hiển thị dữ liệu về chất lượng giấc ngủ trong RecyclerView. Những kỹ thuật mà bạn tìm hiểu được khi xây dựng RecyclerView đầu tiên là đủ cho hầu hết RecyclerViews và hiển thị những danh sách đơn giản có kích thước không quá lớn. Tuy nhiên, có một số kỹ thuật giúp RecyclerView hoạt động hiệu quả hơn cho các danh sách lớn, đồng thời giúp duy trì mã của bạn dễ dàng hơn và mở rộng các danh sách và lưới phức tạp.

Trong lớp học lập trình này, bạn xây dựng trên ứng dụng theo dõi giấc ngủ từ lớp học lập trình trước đó. Bạn có thể tìm hiểu một cách hiệu quả hơn để cập nhật danh sách dữ liệu giấc ngủ và tìm hiểu cách dùng liên kết dữ liệu với RecyclerView. (Nếu bạn không có ứng dụng từ lớp học lập trình trước đó, bạn có thể tải xuống mã dành cho người mới bắt đầu cho lớp học lập trình này.)

Kiến thức bạn cần có

  • Xây dựng giao diện người dùng cơ bản bằng cách sử dụng hoạt động, mảnh và chế độ xem.
  • Di chuyển giữa các mảnh và sử dụng safeArgs để chuyển dữ liệu giữa các mảnh.
  • Xem các mô hình, xem thông tin về nhà máy, phép biến đổi, LiveData và các đối tượng tiếp nhận dữ liệu của các mô hình đó.
  • Cách tạo cơ sở dữ liệu Room, tạo DAO và xác định các thực thể.
  • Cách sử dụng coroutine cho cơ sở dữ liệu và các tác vụ chạy trong thời gian dài khác.
  • Cách triển khai RecyclerView cơ bản bằng bố cục Adapter, ViewHolder và mục.

Kiến thức bạn sẽ học được

  • Cách sử dụng DiffUtil để cập nhật hiệu quả danh sách do RecyclerView hiển thị.
  • Cách sử dụng liên kết dữ liệu với RecyclerView.
  • Cách sử dụng bộ chuyển đổi liên kết để biến đổi dữ liệu.

Bạn sẽ thực hiện

  • Xây dựng dựa trên ứng dụng TrackMySleepChất lượng từ lớp học lập trình trước đó trong loạt bài này.
  • Cập nhật SleepNightAdapter để cập nhật hiệu quả danh sách bằng DiffUtil.
  • Triển khai liên kết dữ liệu cho RecyclerView, sử dụng bộ chuyển đổi liên kết để chuyển đổi dữ liệu.

Ứng dụng theo dõi giấc ngủ có hai màn hình, được biểu thị bằng các mảnh, như minh họa trong hình bên dưới.

Màn hình đầu tiên (hiển thị ở bên trái) có các nút để bắt đầu và dừng theo dõi. Màn hình hiển thị một số dữ liệu giấc ngủ của người dùng. Nút Xóa xóa vĩnh viễn tất cả dữ liệu mà ứng dụng đã thu thập cho người dùng. Màn hình thứ hai hiển thị ở bên phải, cho biết mức chọn điểm xếp hạng chất lượng giấc ngủ.

Ứng dụng này được thiết kế để sử dụng bộ điều khiển giao diện người dùng, ViewModelLiveData, cũng như cơ sở dữ liệu Room để duy trì dữ liệu giấc ngủ.

Dữ liệu giấc ngủ sẽ hiển thị trong RecyclerView. Trong lớp học lập trình này, bạn xây dựng phần DiffUtil và phần liên kết dữ liệu cho RecyclerView. Sau khi tham gia lớp học lập trình này, ứng dụng của bạn sẽ trông giống hệt nhưng sẽ có hiệu quả hơn và dễ dàng mở rộng quy mô hơn.

Bạn có thể tiếp tục dùng ứng dụng SleepTracker trong lớp học lập trình trước đó hoặc tải ứng dụng RecyclerViewDiffUtilDataBinding-Starter xuống từ GitHub.

  1. Nếu cần, hãy tải ứng dụng RecyclerViewDiffUtilDataBinding-Starter xuống từ GitHub và mở dự án trong Android Studio.
  2. Chạy ứng dụng.
  3. Mở tệp SleepNightAdapter.kt.
  4. Hãy kiểm tra mã để làm quen với cấu trúc của ứng dụng. Hãy tham khảo sơ đồ dưới đây để biết thông tin tóm tắt về việc sử dụng RecyclerView với mẫu bộ chuyển đổi để hiển thị dữ liệu giấc ngủ cho người dùng.

  • Dựa trên thông tin người dùng nhập vào, ứng dụng sẽ tạo một danh sách đối tượng SleepNight. Mỗi đối tượng SleepNight đại diện cho một đêm ngủ, thời lượng ngủ và chất lượng của đêm đó.
  • SleepNightAdapter điều chỉnh danh sách các đối tượng SleepNight thành những thành phần mà RecyclerView có thể sử dụng và hiển thị.
  • Bộ chuyển đổi SleepNightAdapter tạo ra ViewHolders chứa các chế độ xem, dữ liệu và thẻ meta cho chế độ xem tuần hoàn dữ liệu để hiển thị dữ liệu.
  • RecyclerView sử dụng SleepNightAdapter để xác định số lượng mục cần hiển thị (getItemCount()). RecyclerView dùng onCreateViewHolder()onBindViewHolder() để kết nối những chủ chế độ xem với dữ liệu.

Phương pháp notificationsDataSetChanged() là không hiệu quả

Để cho RecyclerView biết rằng một mục trong danh sách đã thay đổi và cần được cập nhật, mã hiện tại sẽ gọi notifyDataSetChanged() trong SleepNightAdapter, như minh họa bên dưới.

var data =  listOf<SleepNight>()
   set(value) {
       field = value
       notifyDataSetChanged()
   }

Tuy nhiên, notifyDataSetChanged() sẽ thông báo cho RecyclerView rằng toàn bộ danh sách có thể không hợp lệ. Kết quả là RecyclerView sẽ liên kết lại và vẽ lại mọi mục trong danh sách, kể cả các mục không hiển thị trên màn hình. Đây là rất nhiều công việc không cần thiết. Đối với danh sách lớn hoặc phức tạp, quá trình này có thể mất đủ thời gian để màn hình nhấp nháy hoặc bị giật khi người dùng cuộn qua danh sách.

Để khắc phục vấn đề này, bạn có thể cho RecyclerView biết chính xác điều gì đã thay đổi. Sau đó, RecyclerView chỉ có thể cập nhật các chế độ xem đã thay đổi trên màn hình.

RecyclerView có một API phong phú để cập nhật một phần tử. Bạn có thể sử dụng notifyItemChanged() để thông báo cho RecyclerView rằng một mục đã thay đổi và bạn có thể sử dụng các hàm tương tự cho những mục đã được thêm, xoá hoặc di chuyển. Bạn có thể thực hiện tất cả theo cách thủ công, nhưng tác vụ đó sẽ không quan trọng và có thể liên quan đến khá nhiều mã.

Rất may là có một cách tốt hơn.

DiffUtil hoạt động hiệu quả và hỗ trợ bạn

RecyclerView có một lớp có tên là DiffUtil để tính toán sự khác biệt giữa hai danh sách. DiffUtil lấy danh sách cũ, danh sách mới và tìm hiểu những điểm khác biệt. Công cụ này tìm các mục đã thêm, xoá hoặc thay đổi. Sau đó, Googlebot sử dụng một thuật toán có tên là Eugene W. Thuật toán sự khác biệt của Myers để tìm ra số lượng thay đổi tối thiểu cần thực hiện từ danh sách cũ để tạo danh sách mới.

Sau khi DiffUtil xác định những gì đã thay đổi, RecyclerView có thể sử dụng thông tin đó để chỉ cập nhật những mục đã được thay đổi, thêm, xóa hoặc di chuyển. Điều này hiệu quả hơn nhiều so với việc làm lại toàn bộ danh sách.

Trong tác vụ này, bạn nâng cấp SleepNightAdapter để sử dụng DiffUtil nhằm tối ưu hóa RecyclerView cho các thay đổi đối với dữ liệu.

Bước 1: Triển khai SleepNightDiffCallback

Để dùng chức năng của lớp DiffUtil, hãy mở rộng DiffUtil.ItemCallback.

  1. Mở SleepNightAdapter.kt.
  2. Dưới định nghĩa lớp đầy đủ cho SleepNightAdapter, hãy tạo một lớp cấp cao nhất mới có tên là SleepNightDiffCallback mở rộng DiffUtil.ItemCallback. Chuyển SleepNight dưới dạng một thông số chung.
class SleepNightDiffCallback : DiffUtil.ItemCallback<SleepNight>() {
}
  1. Đặt con trỏ vào tên lớp SleepNightDiffCallback.
  2. Nhấn Alt+Enter (Option+Enter trên máy Mac) và chọn Triển khai thành viên.
  3. Trong hộp thoại mở ra, hãy nhấp chuột trái để chọn phương thức areItemsTheSame()areContentsTheSame(), sau đó nhấp vào OK.

    Thao tác này sẽ tạo các mục trang giữ bên trong SleepNightDiffCallback cho hai phương thức, như minh họa dưới đây. DiffUtil sử dụng hai phương pháp này để tìm ra cách danh sách và các mục đã thay đổi.
    override fun areItemsTheSame(oldItem: SleepNight, newItem: SleepNight): Boolean {
        TODO("not implemented") //To change body of created functions use File | Settings | File Templates.
    }

    override fun areContentsTheSame(oldItem: SleepNight, newItem: SleepNight): Boolean {
        TODO("not implemented") //To change body of created functions use File | Settings | File Templates.
    }
  1. Bên trong areItemsTheSame(), hãy thay thế TODO bằng mã kiểm tra hai mục SleepNight đã chuyển, oldItemnewItem, có giống nhau hay không. Nếu các mục có cùng nightId, thì đó là cùng một mục, vì vậy, hãy trả về true. Nếu không, hãy trả về false. DiffUtil sử dụng thử nghiệm này để giúp khám phá xem một mục đã được thêm, xóa hoặc di chuyển hay chưa.
override fun areItemsTheSame(oldItem: SleepNight, newItem: SleepNight): Boolean {
   return oldItem.nightId == newItem.nightId
}
  1. Bên trong areContentsTheSame(), hãy kiểm tra xem oldItemnewItem có chứa cùng một dữ liệu hay không; tức là chúng có bằng nhau hay không. Bước kiểm tra đẳng thức này sẽ kiểm tra tất cả các trường vì SleepNight là lớp dữ liệu. Các lớp Data sẽ tự động xác định equals và một vài phương pháp khác cho bạn. Nếu có sự khác biệt giữa oldItemnewItem, mã này sẽ thông báo cho DiffUtil rằng mặt hàng đã được cập nhật.
override fun areContentsTheSame(oldItem: SleepNight, newItem: SleepNight): Boolean {
   return oldItem == newItem
}

Đây là một mô hình phổ biến để sử dụng RecyclerView nhằm hiển thị danh sách các nội dung thay đổi. RecyclerView cung cấp một lớp bộ chuyển đổi, ListAdapter, giúp bạn tạo một bộ chuyển đổi RecyclerView có dữ liệu hỗ trợ trong danh sách.

ListAdapter sẽ theo dõi danh sách cho bạn và thông báo cho bộ chuyển đổi khi danh sách được cập nhật.

Bước 1: Thay đổi bộ chuyển đổi để mở rộng ListAdapter

  1. Trong tệp SleepNightAdapter.kt, hãy thay đổi chữ ký của lớp SleepNightAdapter để gia hạn ListAdapter.
  2. Nếu được nhắc, hãy nhập androidx.recyclerview.widget.ListAdapter.
  3. Thêm SleepNight làm đối số đầu tiên cho ListAdapter, trước SleepNightAdapter.ViewHolder.
  4. Thêm SleepNightDiffCallback() làm thông số vào hàm dựng. ListAdapter sẽ sử dụng thông tin này để tìm ra thay đổi trong danh sách. Chữ ký của bạn trong lớp SleepNightAdapter đã hoàn thành sẽ giống như bên dưới.
class SleepNightAdapter : ListAdapter<SleepNight, SleepNightAdapter.ViewHolder>(SleepNightDiffCallback()) {
  1. Bên trong lớp SleepNightAdapter, hãy xóa trường data, bao gồm cả phương thức setter. Bạn không cần dùng nữa vì ListAdapter sẽ theo dõi danh sách cho bạn.
  2. Xóa giá trị ghi đè của getItemCount()ListAdapter sẽ triển khai phương thức này cho bạn.
  3. Để loại bỏ lỗi trong onBindViewHolder(), hãy thay đổi biến item. Thay vì dùng data để nhận item, hãy gọi phương thức getItem(position)ListAdapter cung cấp.
val item = getItem(position)

Bước 2: Sử dụng Gửi danh sách() để luôn cập nhật danh sách

Mã của bạn cần phải thông báo cho ListAdapter khi có một danh sách đã thay đổi. ListAdapter cung cấp một phương thức có tên là submitList() để cho ListAdapter biết rằng có một phiên bản mới của danh sách. Khi phương thức này được gọi, ListAdapter sẽ phân biệt danh sách mới với danh sách cũ và phát hiện các mục đã được thêm, xóa, di chuyển hoặc thay đổi. Sau đó, ListAdapter sẽ cập nhật các mục hiển thị trong RecyclerView.

  1. Mở SleepTrackerFragment.kt.
  2. Trong onCreateView(), trong trình quan sát trên sleepTrackerViewModel, hãy tìm lỗi mà biến data mà bạn\39; đã xóa được tham chiếu.
  3. Thay thế adapter.data = it bằng một lệnh gọi tới adapter.submitList(it). Bạn có thể xem mã cập nhật bên dưới.

sleepTrackerViewModel.nights.observe(viewLifecycleOwner, Observer {
   it?.let {
       adapter.submitList(it)
   }
})
  1. Chạy ứng dụng của bạn. Ứng dụng có thể chạy nhanh hơn và có thể không nhận thấy ngay khi danh sách của bạn nhỏ.

Trong tác vụ này, bạn sử dụng kỹ thuật tương tự như trong các lớp học lập trình trước đây để thiết lập liên kết dữ liệu và loại bỏ các lệnh gọi đến findViewById().

Bước 1: Thêm liên kết dữ liệu vào tệp bố cục

  1. Mở tệp bố cục list_item_sleep_night.xml trong thẻ Văn bản.
  2. Đặt con trỏ vào thẻ ConstraintLayout rồi nhấn Alt+Enter (Option+Enter trên máy Mac). Trình đơn ý định (trình đơn khắc phục nhanh ") sẽ mở ra.
  3. Chọn Chuyển đổi sang bố cục liên kết dữ liệu. Thao tác này sẽ gói bố cục vào <layout> và thêm thẻ <data> bên trong.
  4. Hãy di chuyển trở lại đầu trang (nếu cần) và bên trong thẻ <data>, hãy khai báo một biến có tên là sleep.
  5. Đặt type làm tên đủ điều kiện của SleepNight, com.example.android.trackmysleepquality.database.SleepNight. Thẻ <data> đã hoàn thành của bạn sẽ trông như bên dưới.
   <data>
        <variable
            name="sleep"
            type="com.example.android.trackmysleepquality.database.SleepNight"/>
    </data>
  1. Để buộc tạo đối tượng Binding, hãy chọn Tạo > Dọn sạch dự án, sau đó chọn Tạo &g; Tạo lại dự án. (Nếu bạn vẫn gặp sự cố, hãy chọn Tệp > Vô hiệu hóa bộ nhớ đệm/Khởi động lại.) Đối tượng liên kết ListItemSleepNightBinding, cùng với mã có liên quan, sẽ được thêm vào các tệp do dự án tạo.

Bước 2: Tăng cường bố cục mục bằng cách liên kết dữ liệu

  1. Mở SleepNightAdapter.kt.
  2. Trong lớp ViewHolder, hãy tìm phương thức from().
  3. Xoá phần khai báo của biến view.

Mã cần xóa:

val view = layoutInflater
       .inflate(R.layout.list_item_sleep_night, parent, false)
  1. Trong đó, biến view, xác định một biến mới có tên là binding, làm tăng đối tượng liên kết ListItemSleepNightBinding, như minh họa dưới đây. Thực hiện thao tác nhập cần thiết của đối tượng liên kết.
val binding =
ListItemSleepNightBinding.inflate(layoutInflater, parent, false)
  1. Ở cuối hàm, hãy trả về binding thay vì trả về view.
return ViewHolder(binding)
  1. Để loại bỏ lỗi, hãy đặt con trỏ lên từ binding. Nhấn Alt+Enter (Option+Enter trên một máy Mac) để mở trình đơn ý định.
  1. Chọn Thay đổi thông số ' itemView ' loại hàm dựng chính của lớp ' ViewHolder ' thành ' ListItemSleepNightBinding#39;. Thao tác này sẽ cập nhật loại thông số của lớp ViewHolder.

  1. Di chuyển đến định nghĩa lớp của ViewHolder để xem sự thay đổi trong chữ ký. Bạn thấy lỗi cho itemView vì bạn đã thay đổi itemView thành binding trong phương thức from().

    Trong định nghĩa lớp ViewHolder, hãy nhấp chuột phải vào một trong các lần xuất hiện của itemView rồi chọn Tái cấu trúc > Đổi tên. Đổi tên thành binding.
  2. Thêm tiền tố tham số hàm tạo binding với val để đặt thuộc tính đó thành thuộc tính.
  3. Trong lệnh gọi đến lớp mẹ, RecyclerView.ViewHolder, hãy thay đổi thông số từ binding thành binding.root. Bạn cần chuyển View, còn binding.root là gốc ConstraintLayout trong bố cục mục.
  4. Việc khai báo lớp đã hoàn tất của bạn phải giống như mã bên dưới.
class ViewHolder private constructor(val binding: ListItemSleepNightBinding) : RecyclerView.ViewHolder(binding.root){

Bạn cũng sẽ thấy lỗi cho các lệnh gọi tới findViewById() và khắc phục lỗi này tiếp theo.

Bước 3: Thay thế findViewById()

Bây giờ, bạn có thể cập nhật các thuộc tính sleepLength, qualityqualityImage để sử dụng đối tượng binding thay vì findViewById().

  1. Thay đổi các lệnh khởi tạo của sleepLength, qualityStringqualityImage để dùng chế độ xem của đối tượng binding, như minh họa bên dưới. Sau thời điểm này, mã của bạn sẽ không hiển thị lỗi nào nữa.
val sleepLength: TextView = binding.sleepLength
val quality: TextView = binding.qualityString
val qualityImage: ImageView = binding.qualityImage

Với đối tượng liên kết này, bạn không cần xác định các thuộc tính sleepLength, qualityqualityImage nữa. DataBinding sẽ lưu nội dung tìm kiếm vào bộ nhớ đệm, vì vậy, bạn không cần phải khai báo các thuộc tính này.

  1. Nhấp chuột phải vào tên thuộc tính sleepLength, qualityqualityImage. Chọn Tái cấu trúc và gt; cùng dòng hoặc nhấn Control+Command+N (Option+Command+N trên máy Mac).
  2. Chạy ứng dụng của bạn. (Bạn có thể cần phải Dọn sạchTạo lại dự án nếu có lỗi.)

Trong nhiệm vụ này, bạn nâng cấp ứng dụng của mình để sử dụng liên kết dữ liệu với bộ chuyển đổi liên kết để đặt dữ liệu trong chế độ xem.

Trong một lớp học lập trình trước đây, bạn đã dùng lớp Transformations để lấy LiveData và tạo các chuỗi được định dạng để hiển thị ở chế độ xem văn bản. Tuy nhiên, nếu cần liên kết các loại hoặc các loại phức tạp, bạn có thể cung cấp bộ chuyển đổi liên kết để giúp liên kết dữ liệu sử dụng các loại đó. Bộ chuyển đổi liên kết là bộ chuyển đổi lấy dữ liệu của bạn và điều chỉnh thành một thứ gì đó mà liên kết dữ liệu có thể dùng để liên kết chế độ xem, như văn bản hoặc hình ảnh.

Bạn sẽ triển khai 3 bộ chuyển đổi liên kết, một cho hình ảnh chất lượng và một cho mỗi trường văn bản. Tóm lại, để khai báo bộ chuyển đổi liên kết, bạn xác định một phương thức nhận một mục và chế độ xem, cũng như chú thích mục đó bằng @BindingAdapter. Trong phần nội dung của phương thức, bạn triển khai tính năng chuyển đổi. Trong Kotlin, bạn có thể viết bộ chuyển đổi liên kết dưới dạng hàm mở rộng trên lớp chế độ xem nhận dữ liệu.

Bước 1: Tạo bộ chuyển đổi liên kết

Lưu ý rằng bạn sẽ phải nhập một số lớp học trong bước này và sẽ không được gọi riêng lẻ.

  1. Mở SleepNightAdapater.kt.
  2. Bên trong lớp ViewHolder, hãy tìm phương thức bind() và tự nhắc mình về phương thức này. Bạn sẽ lấy mã tính toán các giá trị cho binding.sleepLength, binding.qualitybinding.qualityImage, đồng thời sử dụng mã này bên trong bộ chuyển đổi. (Còn bây giờ, hãy giữ nguyên mã đó; bạn chuyển mã đó ở bước sau.)
  3. Trong gói sleeptracker, hãy tạo và mở một tệp có tên là BindingUtils.kt.
  4. Khai báo một hàm mở rộng trên TextView, được gọi là setSleepDurationFormatted và chuyển vào SleepNight. Hàm này sẽ là bộ chuyển đổi để tính toán và định dạng thời lượng giấc ngủ.
fun TextView.setSleepDurationFormatted(item: SleepNight) {}
  1. Trong phần nội dung của setSleepDurationFormatted, hãy liên kết dữ liệu với chế độ xem như bạn đã làm trong ViewHolder.bind(). Gọi convertDurationToFormatted() rồi đặt text của TextView thành văn bản được định dạng. (Vì đây là hàm mở rộng trên TextView nên bạn có thể truy cập trực tiếp vào thuộc tính text.)
text = convertDurationToFormatted(item.startTimeMilli, item.endTimeMilli, context.resources)
  1. Để thông báo cho liên kết dữ liệu về bộ chuyển đổi liên kết này, hãy chú thích hàm bằng @BindingAdapter.
  2. Hàm này là bộ chuyển đổi cho thuộc tính sleepDurationFormatted, vì vậy, hãy chuyển sleepDurationFormatted làm đối số cho @BindingAdapter.
@BindingAdapter("sleepDurationFormatted")
  1. Bộ chuyển đổi thứ hai đặt chất lượng giấc ngủ dựa trên giá trị trong đối tượng SleepNight. Tạo một hàm mở rộng có tên là setSleepQualityString() trên TextView và chuyển vào SleepNight.
  2. Trong phần nội dung, hãy liên kết dữ liệu với chế độ xem như bạn đã làm trong ViewHolder.bind(). Gọi convertNumericQualityToString và đặt text.
  3. Chú giải hàm này bằng @BindingAdapter("sleepQualityString").
@BindingAdapter("sleepQualityString")
fun TextView.setSleepQualityString(item: SleepNight) {
   text = convertNumericQualityToString(item.sleepQuality, context.resources)
}
  1. Bộ chuyển đổi liên kết thứ ba đặt hình ảnh ở chế độ xem hình ảnh. Tạo hàm mở rộng trên ImageView, gọi setSleepImage và sử dụng mã trong ViewHolder.bind(), như minh họa bên dưới.
@BindingAdapter("sleepImage")
fun ImageView.setSleepImage(item: SleepNight) {
   setImageResource(when (item.sleepQuality) {
       0 -> R.drawable.ic_sleep_0
       1 -> R.drawable.ic_sleep_1
       2 -> R.drawable.ic_sleep_2
       3 -> R.drawable.ic_sleep_3
       4 -> R.drawable.ic_sleep_4
       5 -> R.drawable.ic_sleep_5
       else -> R.drawable.ic_sleep_active
   })
}

Bước 2: Cập nhật SleepnightAdapter

  1. Mở SleepNightAdapter.kt.
  2. Xóa mọi thứ trong phương thức bind() vì bạn hiện có thể sử dụng liên kết dữ liệu và các bộ chuyển đổi mới để thực hiện việc này.
fun bind(item: SleepNight) {
}
  1. Bên trong bind(), hãy chỉ định giấc ngủ cho item, vì bạn cần cho đối tượng liên kết biết SleepNight mới của bạn.
binding.sleep = item
  1. Bên dưới dòng đó, hãy thêm binding.executePendingBindings(). Cuộc gọi này là tối ưu hóa yêu cầu liên kết dữ liệu để thực thi mọi liên kết đang chờ xử lý ngay lập tức. Bạn nên gọi executePendingBindings() khi sử dụng bộ chuyển đổi liên kết trong RecyclerView vì tính năng này có thể giúp tăng tốc độ của các chế độ xem một chút.
 binding.executePendingBindings()

Bước 3: Thêm liên kết vào bố cục XML

  1. Mở list_item_sleep_night.xml.
  2. Trong ImageView, hãy thêm một thuộc tính app có cùng tên với bộ chuyển đổi liên kết sẽ đặt hình ảnh. Chuyển biến sleep như hình bên dưới.

    Thuộc tính này tạo kết nối giữa chế độ xem và đối tượng liên kết, thông qua bộ chuyển đổi. Mỗi khi được tham chiếu đến sleepImage, bộ chuyển đổi sẽ điều chỉnh dữ liệu từ SleepNight.
app:sleepImage="@{sleep}"
  1. Làm tương tự với các chế độ xem văn bản của sleep_lengthquality_string. Mỗi khi được tham chiếu đến sleepDurationFormatted hoặc sleepQualityString, bộ chuyển đổi sẽ điều chỉnh dữ liệu từ SleepNight.
app:sleepDurationFormatted="@{sleep}"
app:sleepQualityString="@{sleep}"
  1. Chạy ứng dụng của bạn. Ứng dụng hoạt động giống như trước đây. Bộ chuyển đổi liên kết đảm nhận công việc định dạng và cập nhật chế độ xem khi dữ liệu thay đổi, đơn giản hóa ViewHolder và cung cấp mã có cấu trúc tốt hơn nhiều so với trước đây.

Bạn hiện hiển thị cùng một danh sách cho các bài tập thể dục gần đây nhất. Điều đó được thiết kế để cho bạn thấy rằng giao diện Adapter cho phép bạn thiết kế mã của mình theo nhiều cách. Mã của bạn càng phức tạp, bạn càng phải thiết kế kiến trúc tốt. Trong ứng dụng đang tạo, các mẫu này và các ứng dụng khác sẽ được dùng với RecyclerView. Các mẫu đều hoạt động và mỗi mẫu có lợi ích. Lựa chọn nào mà bạn chọn sẽ tùy thuộc vào những gì bạn đang tạo.

Xin chúc mừng! Tại thời điểm này, bạn đã rất thành thạo RecyclerView trên Android.

Dự án Android Studio: RecyclerViewDiffUtilDataBinding.

DiffUtil:

  • RecyclerView có một lớp có tên là DiffUtil để tính toán sự khác biệt giữa hai danh sách.
  • DiffUtil có một lớp có tên là ItemCallBack mà bạn mở rộng để tìm ra sự khác biệt giữa hai danh sách.
  • Trong lớp ItemCallback, bạn phải ghi đè các phương thức areItemsTheSame()areContentsTheSame().

ListAdapter:

  • Để miễn phí quản lý danh sách, bạn có thể sử dụng lớp ListAdapter thay vì RecyclerView.Adapter. Tuy nhiên, nếu sử dụng ListAdapter, bạn phải viết bộ chuyển đổi của riêng mình cho các bố cục khác. Đó là lý do tại sao lớp học lập trình này hướng dẫn bạn cách thực hiện.
  • Để mở trình đơn ý định trong Android Studio, hãy đặt con trỏ vào bất kỳ mục mã nào rồi nhấn Alt+Enter (Option+Enter trên máy Mac). Trình đơn này đặc biệt hữu ích cho việc tái cấu trúc mã và tạo các cuốn sách để triển khai các phương pháp. Trình đơn có giới hạn ngữ cảnh, vì vậy, bạn cần phải đặt con trỏ chính xác để có trình đơn chính xác.

Liên kết dữ liệu:

  • Dùng liên kết dữ liệu trong bố cục mục để liên kết dữ liệu với các chế độ xem.

Bộ chuyển đổi liên kết:

  • Trước đây, bạn đã dùng Transformations để tạo các chuỗi từ dữ liệu. Nếu bạn cần liên kết dữ liệu thuộc các loại phức tạp hoặc khác nhau, hãy cung cấp bộ chuyển đổi liên kết để giúp liên kết dữ liệu sử dụng các loại đó.
  • Để khai báo bộ chuyển đổi liên kết, hãy xác định phương thức lấy một mục và chế độ xem, đồng thời chú thích phương thức đó bằng @BindingAdapter. Trong Kotlin, bạn có thể viết bộ chuyển đổi liên kết dưới dạng hàm mở rộng trên View. Chuyển tên của thuộc tính mà bộ chuyển đổi thích ứng. Ví dụ:
@BindingAdapter("sleepDurationFormatted")
  • Trong bố cục XML, hãy đặt một thuộc tính app có cùng tên với bộ chuyển đổi liên kết. Chuyển một biến có dữ liệu. Ví dụ:
.app:sleepDurationFormatted="@{sleep}"

Khóa học trên Udacity:

Tài liệu dành cho nhà phát triển Android:

Tài nguyên khác:

Phần này liệt kê các bài tập về nhà có thể được giao cho học viên đang làm việc qua lớp học lập trình này trong khóa học do người hướng dẫn tổ chức. Người hướng dẫn có thể làm những việc sau:

  • Giao bài tập về nhà nếu được yêu cầu.
  • Trao đổi với học viên cách nộp bài tập về nhà.
  • Chấm điểm bài tập về nhà.

Người hướng dẫn có thể sử dụng những đề xuất này ít hay nhiều tùy ý. Do đó, họ có thể thoải mái giao bất kỳ bài tập về nhà nào khác mà họ cảm thấy phù hợp.

Nếu bạn đang tự mình làm việc qua lớp học lập trình này, hãy thoải mái sử dụng các bài tập về nhà này để kiểm tra kiến thức của bạn.

Trả lời những câu hỏi này

Câu hỏi 1

Điều nào sau đây là cần thiết để sử dụng DiffUtil? Hãy chọn mọi câu phù hợp.

▢ Mở rộng lớp ItemCallBack.

▢ Ghi đè areItemsTheSame().

▢ Ghi đè areContentsTheSame().

▢ Sử dụng liên kết dữ liệu để theo dõi sự khác biệt giữa các mục.

Câu hỏi 2

Câu nào sau đây đúng về bộ chuyển đổi liên kết?

▢ Bộ chuyển đổi liên kết là hàm được chú thích bằng @BindingAdapter.

▢ Sử dụng bộ chuyển đổi liên kết cho phép bạn tách định dạng dữ liệu khỏi chủ sở hữu chế độ xem.

▢ Bạn phải dùng RecyclerViewAdapter nếu muốn dùng các bộ chuyển đổi liên kết.

▢ Bộ chuyển đổi liên kết là một giải pháp hiệu quả khi bạn cần chuyển đổi dữ liệu phức tạp.

Câu hỏi 3

Khi nào bạn nên cân nhắc sử dụng Transformations thay vì bộ chuyển đổi liên kết? Hãy chọn mọi câu phù hợp.

▢ Dữ liệu của bạn rất đơn giản.

▢ Bạn đang định dạng chuỗi.

▢ Danh sách của bạn rất dài.

ViewHolder của bạn chỉ chứa một chế độ xem.

Bắt đầu bài học tiếp theo: 7.3: GridLayout bằng RecyclerView