paint-brush
RecyclerView를 최적화하는 3가지 빠른 방법by@azamatnurkhojayev
1,015
1,015

RecyclerView를 최적화하는 3가지 빠른 방법

이번 글에서는 RecyclerView를 사용할 때 빠르게 최적화하는 방법을 살펴보고 싶습니다. RecyclerView는 사용자 인터페이스 컴포넌트, 즉 인터페이스에 추가하여 편리하게 목록을 표시할 수 있는 요소입니다.
featured image - RecyclerView를 최적화하는 3가지 빠른 방법
Azamat Nurkhojayev HackerNoon profile picture


안녕하세요 여러분!


이번 글에서는 RecyclerView를 사용할 때 빠르게 최적화하는 방법을 살펴보고 싶습니다.


RecyclerView는 사용자 인터페이스 컴포넌트, 즉 인터페이스에 추가하여 편리하게 목록을 표시할 수 있는 요소입니다. 이는 코드에 내장되어 있으며 목록 표시, 애니메이션 및 최적화를 위한 도구가 이미 포함되어 있으며 사용자 정의 설정도 지원합니다.

RecyclerView의 주요 구성 요소

RecyclerView가 올바르게 작동하려면 다음 구성요소를 구현해야 합니다.

  • 활동 레이아웃에 추가해야 하는 RecyclerView
  • Adapter 데이터를 포함하고 처리하며 목록과 연결합니다.
  • ListAdapter Adapter를 업데이트하고 생성자에서 DiffUtil을 허용합니다.
  • 리소스를 최적화하는 데 사용되며 목록에 포함된 모든 요소에 대한 일종의 컨테이너인 ViewHolder
  • 목록을 최적화하는 데 사용되는 DiffUtil .


처음부터 어떻게 구현하는지 다루지는 않겠습니다. 링크 에서 찾을 수 있습니다.

ViewHolder의 첫 번째 최적화.

바인딩 시 ViewHolder에서 리소스 및 캐스팅에 대한 액세스를 제거합니다.


예를 들어 아래 코드를 살펴보세요.

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


bind 메소드가 호출될 때마다 getStringgetColor 호출이 호출되고 문자열 캐스팅을 위해 toString 도 호출됩니다. 이렇게 하면 더 많은 메모리를 소비하게 됩니다. 그리고 bind 메소드가 빠르게 실행되는지 확인해야 하며, ViewHolder 작업이 목록 항목만 표시하도록 노력해야 합니다. 최적화를 위해 TrackingUiModel 개체에서 eventId , stateText 및 color 문자열을 가져오는 것이 좋습니다.


이제 최적화된 코드는 다음과 같습니다.

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


ViewHolder 이외의 모든 것을 가져왔으며, 필요한 모든 것과 기성품은 TrackingUiModel 에서 수신됩니다.

둘째, ListAdapter와 DiffUtil을 사용하겠습니다.

RecyclerView.Adapter 사용하는 경우 ListAdapter 로 전환하고 DiffUtil 함께 사용해야 합니다.


예를 들어 아래 코드를 살펴보세요.

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


목록 요소를 추가할 때 setItems 메서드를 호출하면 목록이 지워지고 목록 요소가 추가되며 마지막으로 notifyDataSetChanged 호출됩니다.


목록을 변경하거나 업데이트하고 매번 setItems 메서드가 호출된다고 가정해 보겠습니다. 이는 리소스를 많이 소모하게 됩니다. 이러한 이유로 ListAdapterDiffUtil 사용하는 것이 더 좋습니다.


다음은 ListAdapter 구현하고 DiffUtil 사용하는 수정된 TrackingAdapter 입니다. 내 예에서는 TrackingDiffCallback 클래스입니다.


목록을 추가하기 위해 trackingAdapter.submitList 메소드를 사용하고, 내부적으로 어댑터가 목록을 업데이트하는 모든 작업을 수행합니다.


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


셋째, 페이로드를 사용하세요.

목록 항목을 즐겨찾기에 추가하거나, 이미지를 변경하거나, 목록 항목의 다른 보기를 변경하는 등의 경우가 있습니다. 이러한 모든 경우에 변경이 발생하면 ViewHolder 에서 바인딩 메서드가 다시 호출되고 요소의 강조 표시가 시각적으로 표시됩니다. 이런 일이 발생하지 않도록 페이로드가 도움을 요청합니다.


페이로드를 사용하려면 ViewHolder , DiffUtilAdapter 변경해 보겠습니다.


제 경우에는 다음과 같이 변경하겠습니다.


TrackingViewHolder에서 변경해야 하는 데이터가 포함된 바인딩 메서드를 추가합니다.

 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 }


TrackingDiffCallback 에서는 getChangePayload 메서드를 재정의하고 변경되는 필드를 비교합니다. 제 경우에는 formattedTime 입니다.

 override fun getChangePayload(oldItem: TrackingUiModel, newItem: TrackingUiModel): Any? { if (oldItem.formattedTime != newItem.formattedTime) return newItem.formattedTime return super.getChangePayload(oldItem, newItem) }


TrackingListAdapter, 에서는 onBindViewHolder 메서드를 페이로드로 재정의합니다. 페이로드가 비어 있는지 여부를 확인한 후 비어 있지 않으면 첫 번째 요소를 가져오고 페이로드에 대한 바인딩 메서드를 호출합니다.

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



페이로드 없음


페이로드 포함



참조 링크: