Chào mọi người!
Trong bài viết này, tôi muốn xem xét các cách nhanh chóng để tối ưu hóa khi sử dụng RecyclerView.
RecyclerView là một thành phần giao diện người dùng, tức là một thành phần có thể được thêm vào giao diện để hiển thị danh sách một cách thuận tiện. Nó được tích hợp vào mã và đã chứa các công cụ để hiển thị, tạo hiệu ứng động và tối ưu hóa danh sách, đồng thời cũng hỗ trợ các cài đặt tùy chỉnh.
Để RecyclerView hoạt động chính xác, bạn phải triển khai các thành phần sau:
RecyclerView
, phải được thêm vào bố cục Hoạt động của chúng ta;Adapter
, chứa, xử lý và liên kết dữ liệu với danh sách;ListAdapter
, cập nhật Adaptor và chấp nhận DiffUtil trong hàm tạo;ViewHolder
, dùng để tối ưu hóa tài nguyên và là một loại vùng chứa cho tất cả các thành phần có trong danh sách;DiffUtil
, được sử dụng để tối ưu hóa danh sách.
Tôi sẽ không đi sâu vào cách triển khai nó từ đầu, bạn có thể tìm thấy nó tại liên kết .
Xóa quyền truy cập vào tài nguyên và truyền trong ViewHolder khi liên kết.
Ví dụ: lấy mã bên dưới:
data class TrackingUiModel( val id: Long, val title: String, @ColorRes val color: Int, val eventId: Long, val state: TrackingState, val startTime: Long, val endTime: Long, val countTime: Long, val formattedTime: String, ) class TrackingViewHolder(itemView: View): ViewHolder(itemView) { private val binding : TrackingItemBinding by viewBinding() @SuppressLint("SetTextI18n") fun bind(data: TrackingUiModel) { val stateText = when (data.state) { TrackingState.START -> itemView.context.getString(R.string.in_progress) TrackingState.PAUSE -> itemView.context.getString(R.string.pause) else -> { "" } } binding.eventId.text = data.eventId.toString() binding.eventTitle.text = "${data.title} $stateText" binding.eventColor.setBackgroundColor(ContextCompat.getColor(itemView.context, data.color)) binding.countTextView.text = data.formattedTime } }
Mỗi khi phương thức bind
được gọi, các lệnh gọi getString
và getColor
sẽ được gọi và toString
cũng sẽ được gọi để truyền chuỗi. Bằng cách này, chúng ta tiêu thụ nhiều bộ nhớ hơn. Và bạn cần đảm bảo rằng phương thức bind
được thực thi nhanh chóng và bạn nên cố gắng đảm bảo rằng tác vụ ViewHolder
chỉ hiển thị các mục danh sách. Để tối ưu hóa, tốt hơn là lấy chuỗi eventId
, stateText
và color trong đối tượng TrackingUiModel
.
Mã được tối ưu hóa bây giờ trông như thế này:
data class TrackingUiModel( val id: Long, val title: String, val color: Int, val eventId: String, val state: TrackingState, val stateText: String, val startTime: Long, val endTime: Long, val countTime: Long, val formattedTime: String, ) class TrackingViewHolder(itemView: View): ViewHolder(itemView) { private val binding : TrackingItemBinding by viewBinding() fun bind(data: TrackingUiModel) { binding.eventId.text = data.eventId binding.eventTitle.text = "${data.title} ${data.stateText}" binding.eventColor.setBackgroundColor(data.color) binding.countTextView.text = data.formattedTime } }
Chúng tôi đã đưa mọi thứ ra ngoài ViewHolder
, mọi thứ chúng tôi cần và sẵn sàng sẽ được nhận trong TrackingUiModel
.
Nếu bạn đang sử dụng RecyclerView.Adapter
thì bạn nên chuyển sang ListAdapter
và sử dụng DiffUtil
cùng với nó.
Ví dụ: lấy mã bên dưới:
class TrackingAdapter: RecyclerView.Adapter<TrackingViewHolder>(){ private val mItems = mutableListOf<TrackingUiModel>() override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): TrackingViewHolder { return TrackingViewHolder( LayoutInflater.from(parent.context) .inflate(R.layout.tracking_item, parent, false)) } override fun getItemCount(): Int = mItems.size override fun onBindViewHolder(holder: TrackingViewHolder, position: Int) { holder.bind(mItems[position]) } fun setItems(items: List<TrackingUiModel>){ mItems.clear() mItems.addAll(items) notifyDataSetChanged() } }
Khi thêm các thành phần danh sách, việc gọi phương thức setItems
sẽ xóa danh sách, thêm các thành phần danh sách và cuối cùng gọi notifyDataSetChanged
.
Hãy tưởng tượng rằng chúng ta thay đổi hoặc cập nhật danh sách và phương thức setItems
sẽ được gọi mỗi lần; việc này sẽ khá tốn tài nguyên. Vì lý do này, tốt hơn nên sử dụng ListAdapter
và DiffUtil
.
Dưới đây là TrackingAdapter
đã được sửa đổi để triển khai ListAdapter
và sử dụng DiffUtil
; trong ví dụ của tôi, đó là lớp TrackingDiffCallback
.
Để thêm danh sách, chúng tôi sử dụng phương thức trackingAdapter.submitList
và cơ bản, bộ điều hợp sẽ thực hiện tất cả công việc cập nhật danh sách cho chúng tôi.
class TrackingAdapter: ListAdapter<TrackingUiModel, TrackingViewHolder>(TrackingDiffCallback()){ override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): TrackingViewHolder { return TrackingViewHolder( LayoutInflater.from(parent.context) .inflate(R.layout.tracking_item, parent, false)) } override fun onBindViewHolder(holder: TrackingViewHolder, position: Int) { holder.bind(getItem(position)) } }
class TrackingDiffCallback: DiffUtil.ItemCallback<TrackingUiModel>() { override fun areItemsTheSame(oldItem: TrackingUiModel, newItem: TrackingUiModel): Boolean { return oldItem.id == newItem.id } override fun areContentsTheSame(oldItem: TrackingUiModel, newItem: TrackingUiModel): Boolean { return oldItem == newItem } }
Có những trường hợp như thêm mục danh sách vào mục yêu thích, thay đổi hình ảnh hoặc thay đổi một số chế độ xem khác của mục danh sách. Khi các thay đổi xảy ra trong tất cả các trường hợp này, phương thức liên kết sẽ được gọi lại trong ViewHolder
và chúng tôi nhận thấy trực quan phần nổi bật của phần tử. Để ngăn điều này xảy ra, tải trọng sẽ đến với chúng tôi để được trợ giúp.
Để sử dụng tải trọng, hãy thực hiện thay đổi tại ViewHolder
, DiffUtil
và Adapter
.
Trong trường hợp của tôi, tôi sẽ thực hiện những thay đổi sau.
Trong TrackViewHolder, chúng tôi thêm phương thức liên kết với dữ liệu cần thay đổi.
fun bind(data: TrackingUiModel, newTime: String) { binding.eventId.text = data.eventId binding.eventTitle.text = "${data.title} ${data.stateText}" binding.eventColor.setBackgroundColor(data.color) binding.countTextView.text = newTime }
Trong TrackingDiffCallback
, chúng tôi ghi đè phương thức getChangePayload
và so sánh trường đang được thay đổi. Trong trường hợp của tôi, nó được formattedTime
.
override fun getChangePayload(oldItem: TrackingUiModel, newItem: TrackingUiModel): Any? { if (oldItem.formattedTime != newItem.formattedTime) return newItem.formattedTime return super.getChangePayload(oldItem, newItem) }
Trong TrackingListAdapter,
chúng tôi ghi đè phương thức onBindViewHolder
bằng tải trọng. Ta kiểm tra xem payload có rỗng hay không, nếu không trống thì ta lấy phần tử đầu tiên và gọi phương thức liên kết cho payload.
override fun onBindViewHolder(holder: TrackingViewHolder, position: Int, payloads: MutableList<Any>) { if (payloads.isEmpty()) { super.onBindViewHolder(holder, position, payloads) } else { val newTime = payloads.firstOrNull() as? String ?: "" holder.bind(getItem(position), newTime) } }
Liên kết tham khảo: