Lớp học lập trình này thuộc khoá học Kiến thức cơ bản về Kotlin cho Android. Bạn sẽ nhận được nhiều giá trị nhất qua khoá học này nếu thực hiện các lớp học lập trình theo trình tự. Tất cả lớp học lập trình của khoá học đều được liệt kê trên trang đích của lớp học lập trình Kiến thức cơ bản về cách tạo ứng dụng Android bằng Kotlin.
Giới thiệu
Trong lớp học lập trình trước, bạn đã cập nhật ứng dụng TrackMySleepQuality để hiển thị dữ liệu về chất lượng giấc ngủ trong RecyclerView
. Các kỹ thuật mà bạn đã học được khi tạo RecyclerView
đầu tiên là đủ cho hầu hết các RecyclerViews
hiển thị danh sách đơn giản không quá lớn. Tuy nhiên, có một số kỹ thuật giúp RecyclerView
hiệu quả hơn cho các danh sách lớn, đồng thời giúp mã của bạn dễ duy trì hơn và mở rộng cho 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 sẽ phát triển ứng dụng theo dõi giấc ngủ từ lớp học lập trình trước. Bạn sẽ tìm hiểu một cách hiệu quả hơn để cập nhật danh sách dữ liệu về giấc ngủ và tìm hiểu cách sử dụng tính năng liên kết dữ liệu với RecyclerView
. (Nếu không có ứng dụng từ lớp học lập trình trước, bạn có thể tải mã khởi đầ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 một hoạt động, các mảnh và thành phần hiển thị.
- Điều hướng giữa các mảnh và sử dụng
safeArgs
để truyền dữ liệu giữa các mảnh. - Xem các mô hình, nhà máy mô hình hiển thị, các phép biến đổi và
LiveData
cũng như các đối tượng theo dõi của chúng. - 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 một
RecyclerView
cơ bản bằngAdapter
,ViewHolder
và bố cục thành phần.
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 doRecyclerView
hiển thị. - Cách sử dụng tính năng liên kết dữ liệu với
RecyclerView
. - Cách sử dụng trình chuyển đổi liên kết để chuyển đổi dữ liệu.
Bạn sẽ thực hiện
- Xây dựng trên ứng dụng TrackMySleepQuality từ lớp học lập trình trước trong loạt lớp học lập trình này.
- Cập nhật
SleepNightAdapter
để cập nhật danh sách một cách hiệu quả bằng cách sử dụngDiffUtil
. - 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ó 2 màn hình, được biểu thị bằng các mảnh, như trong hình dưới đây.
Màn hình đầu tiên (xuất hiện ở bên trái) có các nút để bắt đầu và dừng theo dõi. Màn hình này cho thấy một số dữ liệu về giấc ngủ của người dùng. Nút Xoá sẽ xoá 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 (ở bên phải) là màn hình chọn mức đánh giá chất lượng giấc ngủ.
Ứng dụng này được thiết kế để sử dụng một bộ điều khiển giao diện người dùng, ViewModel
và LiveData
, cũng như một cơ sở dữ liệu Room
để duy trì dữ liệu giấc ngủ.
Dữ liệu giấc ngủ được hiển thị trong RecyclerView
. Trong lớp học lập trình này, bạn sẽ tạo DiffUtil
và phần liên kết dữ liệu cho RecyclerView
. Sau lớp học lập trình này, ứng dụng của bạn sẽ trông hoàn toàn giống nhau, nhưng sẽ hiệu quả hơn và dễ dàng mở rộng cũng như duy trì hơn.
Bạn có thể tiếp tục sử 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 qua GitHub.
- Nếu cần, hãy tải ứng dụng RecyclerViewDiffUtilDataBinding-Starter xuống từ GitHub rồi mở dự án trong Android Studio.
- Chạy ứng dụng.
- Mở tệp
SleepNightAdapter.kt
. - Kiểm tra mã để làm quen với cấu trúc của ứng dụng. Hãy tham khảo sơ đồ bên dưới để xem lại cách sử dụng
RecyclerView
với mẫu bộ chuyển đổi để hiển thị dữ liệu về giấc ngủ cho người dùng.
- Từ thông tin đầu vào của người dùng, ứng dụng sẽ tạo một danh sách các đối tượng
SleepNight
. Mỗi đối tượngSleepNight
đại diện cho một đêm ngủ, thời lượng và chất lượng của giấc ngủ đó. SleepNightAdapter
điều chỉnh danh sách các đối tượngSleepNight
thành một thứ màRecyclerView
có thể sử dụng và hiển thị.- Trình kết nối
SleepNightAdapter
tạo raViewHolders
chứa các khung hiển thị, dữ liệu và thông tin meta để khung hiển thị trình tái chế hiển thị dữ liệu. RecyclerView
sử dụngSleepNightAdapter
để xác định số lượng mục cần hiển thị (getItemCount()
).RecyclerView
sử dụngonCreateViewHolder()
vàonBindViewHolder()
để lấy các trình giữ khung hiển thị được liên kết với dữ liệu để hiển thị.
Phương thức notifyDataSetChanged() 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 hoạ bên dưới.
var data = listOf<SleepNight>()
set(value) {
field = value
notifyDataSetChanged()
}
Tuy nhiên, notifyDataSetChanged()
cho RecyclerView
biết rằng toàn bộ danh sách có khả năng không hợp lệ. Do đó, RecyclerView
sẽ liên kết lại và vẽ lại mọi mục trong danh sách, kể cả những 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 các danh sách lớn hoặc phức tạp, quá trình này có thể mất nhiều thời gian đến mức màn hình nhấp nháy hoặc 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 những gì đã thay đổi. Sau đó, RecyclerView
chỉ có thể cập nhật những khung hiển thị đã thay đổi trên màn hình.
RecyclerView
có một API đa dạng để cập nhật một phần tử duy nhất. Bạn có thể dùng notifyItemChanged()
để cho RecyclerView
biết rằng một mục đã thay đổi và bạn có thể 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ể làm tất cả theo cách thủ công, nhưng đó là một việc không hề đơn giản và có thể liên quan đến khá nhiều mã.
May mắn thay, có một cách hay hơn.
DiffUtil hoạt động hiệu quả và giúp bạn thực hiện những công việc khó khăn
RecyclerView
có một lớp gọi là DiffUtil
dùng để tính toán sự khác biệt giữa hai danh sách. DiffUtil
lấy một danh sách cũ và một danh sách mới rồi tìm ra điểm khác biệt. Công cụ này tìm thấy những mục đã được thêm, xoá hoặc thay đổi. Sau đó, hệ thống sẽ sử dụng một thuật toán có tên là Eugene W. Thuật toán chênh lệch 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 ra danh sách mới.
Sau khi DiffUtil
xác định được những thay đổi, RecyclerView
có thể sử dụng thông tin đó để chỉ cập nhật những mục đã thay đổi, được thêm, bị xoá 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 nhiệm vụ này, bạn sẽ nâng cấp SleepNightAdapter
để dùng DiffUtil
nhằm tối ưu hoá RecyclerView
cho các thay đổi đối với dữ liệu.
Bước 1: Triển khai SleepNightDiffCallback
Để sử dụng chức năng của lớp DiffUtil
, hãy mở rộng DiffUtil.ItemCallback
.
- Mở
SleepNightAdapter.kt
. - Bên dưới định nghĩa đầy đủ về lớp cho
SleepNightAdapter
, hãy tạo một lớp cấp cao mới có tên làSleepNightDiffCallback
, giúp mở rộngDiffUtil.ItemCallback
. TruyềnSleepNight
dưới dạng tham số chung.
class SleepNightDiffCallback : DiffUtil.ItemCallback<SleepNight>() {
}
- Đặt con trỏ vào tên lớp
SleepNightDiffCallback
. - Nhấn
Alt+Enter
(Option+Enter
trên máy Mac) rồi chọn Implement Members (Triển khai thành viên). - Trong hộp thoại mở ra, hãy nhấp vào Shift + nhấp chuột trái để chọn các phương thức
areItemsTheSame()
vàareContentsTheSame()
, sau đó nhấp vào OK.
Thao tác này sẽ tạo các đoạn mã sơ khai bên trongSleepNightDiffCallback
cho hai phương thức, như minh hoạ bên dưới.DiffUtil
sử dụng 2 phương thức này để xác định 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.
}
- Trong
areItemsTheSame()
, hãy thay thếTODO
bằng mã kiểm thử xem 2 mụcSleepNight
được truyền vào,oldItem
vànewItem
, có giống nhau hay không. Nếu các mục có cùngnightId
, 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 kiểm thử này để giúp phát hiện xem một mục đã được thêm, xoá hay di chuyển.
override fun areItemsTheSame(oldItem: SleepNight, newItem: SleepNight): Boolean {
return oldItem.nightId == newItem.nightId
}
- Bên trong
areContentsTheSame()
, hãy kiểm tra xemoldItem
vànewItem
có chứa cùng một dữ liệu hay không, tức là liệu chúng có bằng nhau hay không. Thao tác kiểm tra tính bình đẳng này sẽ kiểm tra tất cả các trường, vìSleepNight
là một lớp dữ liệu. Các lớpData
sẽ tự động xác địnhequals
và một số phương thức khác cho bạn. Nếu có sự khác biệt giữaoldItem
vànewItem
, mã này sẽ choDiffUtil
biết 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ẫu hình phổ biến khi dùng RecyclerView
để hiển thị một danh sách có 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 hỗ trợ bởi một danh sách.
ListAdapter
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
- Trong tệp
SleepNightAdapter.kt
, hãy thay đổi chữ ký lớp củaSleepNightAdapter
để mở rộngListAdapter
. - Nếu được nhắc, hãy nhập
androidx.recyclerview.widget.ListAdapter
. - Thêm
SleepNight
làm đối số đầu tiên vàoListAdapter
, trướcSleepNightAdapter.ViewHolder
. - Thêm
SleepNightDiffCallback()
làm tham số vào hàm khởi tạo.ListAdapter
sẽ sử dụng tham số này để xác định thay đổi trong danh sách. Chữ ký lớpSleepNightAdapter
đã hoàn tất của bạn sẽ có dạng như dưới đây.
class SleepNightAdapter : ListAdapter<SleepNight, SleepNightAdapter.ViewHolder>(SleepNightDiffCallback()) {
- Bên trong lớp
SleepNightAdapter
, hãy xoá trườngdata
, bao gồm cả phương thức thiết lập. Bạn không cần đến nó nữa vìListAdapter
sẽ theo dõi danh sách cho bạn. - Xoá chế độ ghi đè
getItemCount()
, vìListAdapter
sẽ triển khai phương thức này cho bạn. - Để loại bỏ lỗi trong
onBindViewHolder()
, hãy thay đổi biếnitem
. Thay vì dùngdata
để lấyitem
, hãy gọi phương thứcgetItem(position)
màListAdapter
cung cấp.
val item = getItem(position)
Bước 2: Sử dụng submitList() để cập nhật danh sách
Mã của bạn cần cho ListAdapter
biết khi có 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ẽ so sánh danh sách mới với danh sách cũ và phát hiện các mục đã được thêm, xoá, di chuyển hoặc thay đổi. Sau đó, ListAdapter
sẽ cập nhật các mục mà RecyclerView
hiển thị.
- Mở
SleepTrackerFragment.kt
. - Trong
onCreateView()
, trong trình theo dõi trênsleepTrackerViewModel
, hãy tìm lỗi mà biếndata
bạn đã xoá được tham chiếu. - Thay thế
adapter.data = it
bằng một lệnh gọi đếnadapter.submitList(it)
. Mã đã cập nhật được trình bày bên dưới.
sleepTrackerViewModel.nights.observe(viewLifecycleOwner, Observer {
it?.let {
adapter.submitList(it)
}
})
- Chạy ứng dụng. Ứng dụng sẽ chạy nhanh hơn, có thể không đáng kể nếu danh sách của bạn nhỏ.
Trong nhiệm vụ này, bạn sẽ sử dụng cùng một kỹ thuật như trong các lớp học lập trình trước để 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
- Mở tệp bố cục
list_item_sleep_night.xml
trong thẻ Văn bản. - Đặt con trỏ lên thẻ
ConstraintLayout
rồi nhấnAlt+Enter
(Option+Enter
trên máy Mac). Trình đơn ý định (trình đơn "khắc phục nhanh") sẽ mở ra. - Chọn Chuyển đổi thành bố cục liên kết dữ liệu. Thao tác này sẽ bao bọc bố cục trong
<layout>
và thêm một thẻ<data>
vào bên trong. - Nếu cần, hãy di chuyển lên đầu và bên trong thẻ
<data>
, hãy khai báo một biến có tên làsleep
. - Đặt
type
thành tên đủ điều kiện củaSleepNight
,com.example.android.trackmysleepquality.database.SleepNight
. Thẻ<data>
đã hoàn tất của bạn sẽ có dạng như dưới đây.
<data>
<variable
name="sleep"
type="com.example.android.trackmysleepquality.database.SleepNight"/>
</data>
- Để buộc tạo đối tượng
Binding
, hãy chọn Build > Clean Project (Xây dựng > Dọn dự án), sau đó chọn Build > Rebuild Project (Xây dựng > Tạo lại dự án). (Nếu bạn vẫn gặp vấn đề, hãy chọn File > Invalidate Caches / Restart (Tệp > Xoá bộ nhớ đệm/Khởi động lại).) Đối tượng liên kếtListItemSleepNightBinding
, cùng với mã liên quan, sẽ được thêm vào các tệp đã tạo của dự án.
Bước 2: Tăng kích thước bố cục mục bằng cách sử dụng tính năng liên kết dữ liệu
- Mở
SleepNightAdapter.kt
. - Trong lớp
ViewHolder
, hãy tìm phương thứcfrom()
. - Xoá khai báo của biến
view
.
Mã cần xoá:
val view = layoutInflater
.inflate(R.layout.list_item_sleep_night, parent, false)
- Xác định một biến mới có tên là
binding
tại vị trí của biếnview
. Biến này sẽ mở rộng đối tượng liên kếtListItemSleepNightBinding
, như minh hoạ bên dưới. Nhập đối tượng liên kết cần thiết.
val binding =
ListItemSleepNightBinding.inflate(layoutInflater, parent, false)
- Ở cuối hàm, thay vì trả về
view
, hãy trả vềbinding
.
return ViewHolder(binding)
- Để loại bỏ lỗi này, hãy đặt con trỏ lên từ
binding
. NhấnAlt+Enter
(Option+Enter
trên máy Mac) để mở trình đơn ý định.
- Chọn Thay đổi loại tham số "itemView" của hàm khởi tạo chính của lớp "ViewHolder" thành "ListItemSleepNightBinding". Thao tác này sẽ cập nhật loại tham số của lớp
ViewHolder
.
- Di chuyển lên phần định nghĩa lớp của
ViewHolder
để xem thay đổi trong chữ ký. Bạn sẽ thấy lỗi choitemView
vì bạn đã thay đổiitemView
thànhbinding
trong phương thứcfrom()
.
Trong định nghĩa lớpViewHolder
, hãy nhấp chuột phải vào một trong các lần xuất hiện củaitemView
rồi chọn Refactor > Rename (Tái cấu trúc > Đổi tên). Đổi tên thànhbinding
. - Thêm tiền tố
val
vào tham số hàm khởi tạobinding
để biến tham số này thành một thuộc tính. - Trong lệnh gọi đến lớp mẹ
RecyclerView.ViewHolder
, hãy thay đổi tham số từbinding
thànhbinding.root
. Bạn cần truyền mộtView
vàbinding.root
làConstraintLayout
gốc trong bố cục mục. - Khai báo lớp đã hoàn tất của bạn sẽ có dạng như mã bên dưới.
class ViewHolder private constructor(val binding: ListItemSleepNightBinding) : RecyclerView.ViewHolder(binding.root){
Bạn cũng thấy một lỗi cho các lệnh gọi đến findViewById()
và bạn sẽ khắc phục lỗi này ở bước tiếp theo.
Bước 3: Thay thế findViewById()
Giờ đây, bạn có thể cập nhật các thuộc tính sleepLength
, quality
và qualityImage
để sử dụng đối tượng binding
thay vì findViewById()
.
- Thay đổi quá trình khởi chạy
sleepLength
,qualityString
vàqualityImage
để sử dụng các khung hiển thị của đối tượngbinding
, như minh hoạ bên dưới. Sau bước này, mã của bạn sẽ không còn lỗi nào nữa.
val sleepLength: TextView = binding.sleepLength
val quality: TextView = binding.qualityString
val qualityImage: ImageView = binding.qualityImage
Khi đã có đối tượng liên kết, bạn không cần xác định các thuộc tính sleepLength
, quality
và qualityImage
nữa. DataBinding
sẽ lưu các tìm kiếm vào bộ nhớ đệm, nên bạn không cần khai báo các thuộc tính này.
- Nhấp chuột phải vào tên tài sản
sleepLength
,quality
vàqualityImage
. Chọn Refactor > Inline (Tái cấu trúc > Nội tuyến) hoặc nhấnControl+Command+N
(Option+Command+N
trên máy Mac). - Chạy ứng dụng của bạn. (Bạn có thể cần Dọn dẹp và Tạo lại dự án nếu dự án đó có lỗi.)
Trong nhiệm vụ này, bạn sẽ nâng cấp ứng dụng để sử dụng tính năng liên kết dữ liệu với các phương thức điều hợp liên kết nhằm đặt dữ liệu trong các khung hiển thị.
Trong một lớp học lập trình trước, bạn đã dùng lớp Transformations
để lấy LiveData
và tạo các chuỗi được định dạng để hiển thị trong các khung hiển thị văn bản. Tuy nhiên, nếu cần liên kết các loại khác nhau hoặc các loại phức tạp, bạn có thể cung cấp các phương thức điều hợp liên kết để giúp tính năng liên kết dữ liệu sử dụng các loại đó. Bộ chuyển đổi liên kết là những bộ chuyển đổi lấy dữ liệu của bạn và điều chỉnh dữ liệu đó thành một thứ mà tính năng liên kết dữ liệu có thể dùng để liên kết một thành phần hiển thị, chẳng hạn 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 một bộ chuyển đổi liên kết, bạn hãy xác định một phương thức nhận một mục và một khung hiển thị, rồi chú thích phương thức đó bằng @BindingAdapter
. Trong phần nội dung của phương thức, bạn sẽ triển khai quá trình chuyển đổi. Trong Kotlin, bạn có thể viết một bộ chuyển đổi liên kết dưới dạng một hàm mở rộng trên lớp khung hiển thị nhận dữ liệu.
Bước 1: Tạo các bộ chuyển đổi liên kết
Xin lưu ý rằng bạn sẽ phải nhập một số lớp trong bước này và các lớp đó sẽ không được gọi riêng lẻ.
- Mở
SleepNightAdapater.kt
. - Bên trong lớp
ViewHolder
, hãy tìm phương thứcbind()
và tự nhắc nhở bản thân về chức năng của phương thức này. Bạn sẽ lấy mã tính toán các giá trị chobinding.sleepLength
,binding.quality
vàbinding.qualityImage
, rồi sử dụng mã đó bên trong trình chuyển đổi. (Hiện tại, hãy giữ nguyên mã; bạn sẽ di chuyển mã này trong một bước sau.) - Trong gói
sleeptracker
, hãy tạo và mở một tệp có tên làBindingUtils.kt
. - Khai báo một hàm mở rộng trên
TextView
, có tên làsetSleepDurationFormatted
và truyền vào mộtSleepNight
. Hàm này sẽ là bộ chuyển đổi để tính toán và định dạng thời lượng ngủ.
fun TextView.setSleepDurationFormatted(item: SleepNight) {}
- Trong phần nội dung của
setSleepDurationFormatted
, hãy liên kết dữ liệu với khung hiển thị như bạn đã làm trongViewHolder.bind()
. GọiconvertDurationToFormatted()
rồi đặttext
củaTextView
thành văn bản được định dạng. (Vì đây là một hàm mở rộng trênTextView
, nên bạn có thể truy cập trực tiếp vào thuộc tínhtext
.)
text = convertDurationToFormatted(item.startTimeMilli, item.endTimeMilli, context.resources)
- Để cho biết về phương thức điều hợp liên kết này, hãy chú giải hàm bằng
@BindingAdapter
. - Hàm này là trình chuyển đổi cho thuộc tính
sleepDurationFormatted
, vì vậy, hãy truyềnsleepDurationFormatted
làm đối số cho@BindingAdapter
.
@BindingAdapter("sleepDurationFormatted")
- Bộ chuyển đổi thứ hai đặt chất lượng giấc ngủ dựa trên giá trị trong một đối tượng
SleepNight
. Tạo một hàm mở rộng có tên làsetSleepQualityString()
trênTextView
rồi truyềnSleepNight
vào. - Trong phần nội dung, hãy liên kết dữ liệu với khung hiển thị như bạn đã làm trong
ViewHolder.bind()
. GọiconvertNumericQualityToString
và đặttext
. - Chú giải hàm này bằng
@BindingAdapter("sleepQualityString")
.
@BindingAdapter("sleepQualityString")
fun TextView.setSleepQualityString(item: SleepNight) {
text = convertNumericQualityToString(item.sleepQuality, context.resources)
}
- Phương thức điều hợp liên kết thứ ba sẽ đặt hình ảnh trên một thành phần hiển thị hình ảnh. Tạo hàm mở rộng trên
ImageView
, gọisetSleepImage
và sử dụng mã từViewHolder.bind()
, như minh hoạ 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
- Mở
SleepNightAdapter.kt
. - Xoá mọi thứ trong phương thức
bind()
, vì giờ đây bạn có thể sử dụng tính năng liên kết dữ liệu và các bộ chuyển đổi mới để thực hiện công việc này.
fun bind(item: SleepNight) {
}
- Bên trong
bind()
, hãy chỉ định trạng thái ngủ choitem
, vì bạn cần cho đối tượng liên kết biết vềSleepNight
mới.
binding.sleep = item
- Bên dưới dòng đó, hãy thêm
binding.executePendingBindings()
. Lệnh gọi này là một hoạt động tối ưu hoá yêu cầu liên kết dữ liệu thực thi ngay mọi liên kết đang chờ xử lý. Bạn nên gọiexecutePendingBindings()
khi sử dụng bộ chuyển đổi liên kết trongRecyclerView
, vì thao tác này có thể tăng tốc độ điều chỉnh kích thước các thành phần hiển thị một chút.
binding.executePendingBindings()
Bước 3: Thêm các liên kết vào bố cục XML
- Mở
list_item_sleep_night.xml
. - Trong
ImageView
, hãy thêm một thuộc tínhapp
có cùng tên với phương thức điều hợp liên kết đặt hình ảnh. Truyền biếnsleep
vào, như minh hoạ bên dưới.
Thuộc tính này tạo mối kết nối giữa khung hiển thị và đối tượng liên kết thông qua bộ điều hợp. Bất cứ khi nàosleepImage
được tham chiếu, bộ chuyển đổi sẽ điều chỉnh dữ liệu từSleepNight
.
app:sleepImage="@{sleep}"
- Làm tương tự cho khung hiển thị văn bản
sleep_length
vàquality_string
. Bất cứ khi nàosleepDurationFormatted
hoặcsleepQualityString
được tham chiếu, các bộ chuyển đổi sẽ điều chỉnh dữ liệu từSleepNight
.
app:sleepDurationFormatted="@{sleep}"
app:sleepQualityString="@{sleep}"
- Chạy ứng dụng của bạn. Ứng dụng sẽ hoạt động giống hệt như trước đây. Các bộ chuyển đổi liên kết sẽ đảm nhiệm mọi công việc định dạng và cập nhật các khung hiển thị khi dữ liệu thay đổi, đơn giản hoá
ViewHolder
và mang lại cấu trúc tốt hơn nhiều cho mã so với trước đây.
Bạn đã hiển thị cùng một danh sách cho một số bài tập gần đây. Đó là theo thiết kế, nhằm cho bạn thấy rằng giao diện Adapter
cho phép bạn thiết kế mã theo nhiều cách khác nhau. Mã của bạn càng phức tạp thì việc thiết kế mã một cách hiệu quả càng trở nên quan trọng. Trong các ứng dụng phát hành công khai, những mẫu này và các mẫu khác được dùng với RecyclerView
. Tất cả các mẫu đều hoạt động và mỗi mẫu đều có lợi ích riêng. Bạn chọn loại nào tuỳ thuộc vào nội dung bạn đang xây dựng.
Xin chúc mừng! Đến đây, bạn đã gần như nắm vững RecyclerView
trên Android.
Dự án Android Studio: RecyclerViewDiffUtilDataBinding.
DiffUtil
:
RecyclerView
có một lớp gọi làDiffUtil
dùng để tính toán sự khác biệt giữa hai danh sách.DiffUtil
có một lớp 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ứcareItemsTheSame()
vàareContentsTheSame()
.
ListAdapter
:
- Để quản lý một số danh sách miễn phí, bạn có thể dùng lớp
ListAdapter
thay vìRecyclerView.Adapter
. Tuy nhiên, nếu sử dụngListAdapter
, bạn phải tự viết bộ chuyển đổi cho các bố cục khác. Đó là lý do 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ỏ lên bất kỳ mục nào trong mã rồi nhấn
Alt+Enter
(Option+Enter
trên máy Mac). Trình đơn này đặc biệt hữu ích khi tái cấu trúc mã và tạo các phần giữ chỗ để triển khai phương thức. Trình đơn này phụ thuộc vào bối cảnh, vì vậy bạn cần đặt con trỏ chính xác để có được trình đơn phù hợp.
Liên kết dữ liệu:
- Sử dụng tính nă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 khung hiển thị.
Bộ chuyển đổi liên kết:
- Trước đây, bạn đã dùng
Transformations
để tạo chuỗi từ dữ liệu. Nếu cần liên kết dữ liệu thuộc nhiều loại hoặc loại phức tạp, hãy cung cấp các bộ chuyển đổi liên kết để giúp tính năng liên kết dữ liệu sử dụng các loại đó. - Để khai báo một phương thức điều hợp liên kết, hãy xác định một phương thức lấy một mục và một khung hiển thị, rồi chú thích phương thức đó bằng
@BindingAdapter
. Trong Kotlin, bạn có thể viết phương thức điều hợp liên kết dưới dạng một hàm mở rộng trênView
. Truyền tên của thuộc tính mà lớp chuyển đổi điều chỉnh. 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 trình liên kết bộ điều hợp. Truyền một biến có dữ liệu. Ví dụ:
.app:sleepDurationFormatted="@{sleep}"
Các khoá học của Udacity:
Tài liệu dành cho nhà phát triển Android:
- Tạo một danh sách bằng RecyclerView
RecyclerView
DiffUtil
- Thư viện liên kết dữ liệu
- Các phương thức điều hợp liên kết (binding adapter)
notifyDataSetChanged()
Transformations
Tài nguyên khác:
Phần này liệt kê các bài tập về nhà cho học viên của lớp học lập trình này trong phạm vi khoá học có người hướng dẫn. Người hướng dẫn phải thực hiện các việc sau đây:
- Giao bài tập về nhà nếu cần.
- Trao đổi với học viên về 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 các đề xuất này ít hoặc nhiều tuỳ ý và nên giao cho học viên 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ự học các lớp học lập trình, hãy sử dụng những bài tập về nhà này để kiểm tra kiến thức của mình.
Trả lời các câu hỏi sau
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 trả lời phù hợp.
▢ Mở rộng lớp ItemCallBack
.
▢ Ghi đè areItemsTheSame()
.
▢ Ghi đè areContentsTheSame()
.
▢ Sử dụng tính năng liên kết dữ liệu để theo dõi những điểm khác biệt giữa các mục.
Câu hỏi 2
Câu nào sau đây là đúng về bộ chuyển đổi liên kết?
▢ Trình điều hợp liên kết là một hàm được chú giải bằng @BindingAdapter
.
▢ Khi dùng một trình chuyển đổi liên kết, bạn có thể tách riêng định dạng dữ liệu với chế độ xem chủ sở hữu.
▢ Bạn phải sử 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 trả lời phù hợp.
▢ Dữ liệu của bạn rất đơn giản.
▢ Bạn đang định dạng một chuỗi.
▢ Danh sách của bạn rất dài.
▢ ViewHolder
của bạn chỉ chứa một khung hiển thị.
Bắt đầu bài học tiếp theo: