paint-brush
3 cách nhanh chóng để tối ưu hóa RecyclerViewtừ tác giả@azamatnurkhojayev
1,147 lượt đọc
1,147 lượt đọc

3 cách nhanh chóng để tối ưu hóa RecyclerView

từ tác giả Azamat Nurkhojayev7m2024/05/14
Read on Terminal Reader

dài quá đọc không nổ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.
featured image - 3 cách nhanh chóng để tối ưu hóa RecyclerView
Azamat Nurkhojayev HackerNoon profile picture


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.

Các thành phần chính của RecyclerView

Để 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 .

Tối ưu hóa đầu tiên của ViewHolder.

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 getStringgetColor 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 .

Thứ hai, chúng tôi sẽ sử dụng ListAdapter và DiffUtil.

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 ListAdapterDiffUtil .


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 } }


Thứ ba, Sử dụng tải trọng.

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 , DiffUtilAdapter .


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) } } 



Không có tải trọng


Với tải trọng



Liên kết tham khảo: