본문 바로가기

안드로이드/성능 개선

[안드로이드] RecyclerView 성능 개선 방법 1

OnBindViewHolder에서 바인딩 시 리스너를 반복해서 등록하지 않기

리사이클러뷰는 리스트뷰와는 다르게 재활용을 통해 성능을 개선시킨 라이브러리이다. 재활용 과정에서OnBindViewHolder가 호출되게 되는데 바인딩 과정에서 같은 리스너를 계속 등록하는 것은 성능의 하락으로 이어진다. 따라서, 뷰홀더가 생성될 때 한번만 등록하도록 코드를 리팩토링한다. 뷰홀더에서 View.OnClickListener를 상속받는 방향으로 코드를 수정한다.

 

 

기존 코드

package com.bluemap.overcom_blue.adapter

import android.view.LayoutInflater
import android.view.ViewGroup
import androidx.recyclerview.widget.RecyclerView
import com.bluemap.overcom_blue.model.DiagnosisModel
import com.bluemap.overcom_blue.R
import com.bluemap.overcom_blue.databinding.ItemDiagnosisBinding
import kotlinx.android.synthetic.main.item_diagnosis.view.*

class DiagnosisAdapter(val list: ArrayList<DiagnosisModel>) : RecyclerView.Adapter<DiagnosisAdapter.ViewHolder>() {
    override fun onBindViewHolder(holder: ViewHolder, position: Int) {
        holder.bind(list[position])
    }
	...

    inner class ViewHolder(val binding: ItemDiagnosisBinding) : RecyclerView.ViewHolder(binding.root){
        fun bind(model: DiagnosisModel){
            binding.model=model
            binding.root.so_disagree_btn.setOnClickListener {
                initImage()
                binding.root.so_disagree_btn.setImageResource(R.drawable.diagnosis_btn_clicked)
                list[binding.root.tag as Int].point=0
            }
            binding.root.disagree_btn.setOnClickListener {
                initImage()
                binding.root.disagree_btn.setImageResource(R.drawable.diagnosis_btn_clicked)
                list[binding.root.tag as Int].point=1
            }
            binding.root.agree_btn.setOnClickListener {
                initImage()
                binding.root.agree_btn.setImageResource(R.drawable.diagnosis_btn_clicked)
                list[binding.root.tag as Int].point=2
            }
            binding.root.so_agree_btn.setOnClickListener {
                initImage()
                binding.root.so_agree_btn.setImageResource(R.drawable.diagnosis_btn_clicked)
                list[binding.root.tag as Int].point=3
            }
        }

        private fun initImage(){
            binding.root.so_disagree_btn.setImageResource(R.drawable.diagnosis_btn)
            binding.root.disagree_btn.setImageResource(R.drawable.diagnosis_btn)
            binding.root.agree_btn.setImageResource(R.drawable.diagnosis_btn)
            binding.root.so_agree_btn.setImageResource(R.drawable.diagnosis_btn)
        }
    }


}

위는 바인딩마다 리스너가 계속 등록되게 된다.

 

리팩토링된 코드

class DiagnosisAdapter(
        val list: ArrayList<DiagnosisModel>,
        private val fragment: DiagnosisFragment)
    : RecyclerView.Adapter<RecyclerView.ViewHolder>() {

    override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {
        holder.bind(list[position])
    }
	...
    inner class ItemViewHolder(val binding: ItemDiagnosisBinding) : RecyclerView.ViewHolder(binding.root), View.OnClickListener{
        init {
            binding.root.so_disagree_btn.setOnClickListener (this)
            binding.root.disagree_btn.setOnClickListener (this)
            binding.root.agree_btn.setOnClickListener (this)
            binding.root.so_agree_btn.setOnClickListener (this)

        }
        fun bind(model: DiagnosisModel){
            binding.model=model
        }

        override fun onClick(v: View) {
            initImage()
            (v as ImageView).setImageResource(R.drawable.diagnosis_btn_clicked)
            when(v.id){
                R.id.so_disagree_btn -> list[binding.root.tag as Int].point=0
                R.id.disagree_btn -> list[binding.root.tag as Int].point=1
                R.id.agree_btn -> list[binding.root.tag as Int].point=2
                R.id.so_agree_btn -> list[binding.root.tag as Int].point=3
            }
        }

        private fun initImage(){
            binding.root.so_disagree_btn.setImageResource(R.drawable.diagnosis_btn)
            binding.root.disagree_btn.setImageResource(R.drawable.diagnosis_btn)
            binding.root.agree_btn.setImageResource(R.drawable.diagnosis_btn)
            binding.root.so_agree_btn.setImageResource(R.drawable.diagnosis_btn)
        }
    }
}

위는 뷰홀더가 생성될 때 단 한번만 리스너를 등록한다. 이 과정에서 bind 메서드의 model을 활용할 수 없으므로 adapterPosition을 활용한다.