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을 활용한다.