简体   繁体   中英

How to provide an implementation of the click listener interface for the recycler adapter?

Suppose we have a recycler view in a fragment, and we need to add a click handler to the adapter. Which one of the following approaches are better? Please tell me the pros and cons each one:

Approach One: implementing OnItemClickListener in the fragment:

In this approach we implement the OnItemClickListener in the fragment class MyFragment: Fragment(), MyAdapter.OnItemClickListener and pass it to the adapter using this keyword:

class MyFragment : Fragment() , MyAdapter.OnItemClickListener {

override fun onCreateView(
    inflater: LayoutInflater, container: ViewGroup?,
    savedInstanceState: Bundle?
): View? {
        // Inflate the layout for this fragment
        myRecyclerView.adapter = MyAdapter(it , this) // here we pass the fragment 
    
        return view
    }

    override fun onCLick(item: MyItem) {
        Log.d("test","item name = " + item.Name)
    }
}

class MyAdapter(val data: List<MyItem> , val onItemClickListener: OnItemClickListener) :
RecyclerView.Adapter<MyFragment.MyViewHolder>() {
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): MyViewHolder {
        val view = LayoutInflater.from(parent.context)
            .inflate(R.layout.support_simple_spinner_dropdown_item, parent, false)
        return MyViewHolder(view)
    }

    override fun getItemCount() = data.size

    override fun onBindViewHolder(holder: MyViewHolder, position: Int) {
        holder.itemView.setOnClickListener{
            onItemClickListener.onCLick(data[position])
        }
        with(holder) {
            textView1?.let {
                it.text = data[position].Name
            }
        }
    }

    interface OnItemClickListener{
        fun onCLick(item: MyItem)
    }
}

Approach Two: implementing OnItemClickListener using a anonymous object:

In this approach we use object: syntax to create an anonymous object, like inline interface implementation in java:

class MyFragment : Fragment(){
override fun onCreateView(
    inflater: LayoutInflater, container: ViewGroup?,
    savedInstanceState: Bundle?
    ): View? {
        // Inflate the layout for this fragment
        myRecyclerView.adapter = MyAdapter(it ,object : MyAdapter.OnItemClickListener{
            override fun onCLick(item: MyItem) {
                Log.d("test","name = " + item.Name)
            }
        })
        return view
    }

class MyAdapter(val data: List<MyItem> , val onItemClickListener: OnItemClickListener) :
    RecyclerView.Adapter<MyViewHolder>() {
    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): MyViewHolder {
        val view = LayoutInflater.from(parent.context)
            .inflate(R.layout.support_simple_spinner_dropdown_item, parent, false)
        return MyViewHolder(view)
    }

    override fun getItemCount() = data.size

    override fun onBindViewHolder(holder: MyViewHolder, position: Int) {
        holder.itemView.setOnClickListener{
            onItemClickListener.onCLick(data[position])
        }
        with(holder) {
            textView1?.let {
                it.text = data[position].Name
            }
        }
    }

    interface OnItemClickListener{
        fun onCLick(item: MyItem)
    }
}
  1. Which one is more modern?
  2. Which one gives a better performance? (If there is any difference)
  3. Why in most tutorials and video lessons they use the first approach, while the second one needs fewer lines of codes?

Simple answer: it doesn't matter. Approach 2 simply allocates one extra object on the heap which is not a big deal. I would prefer 2 over 1 because 1 exposes that MyFragment implements MyAdapter.OnItemClickListener : that should remain an implementation detail, not a part of your fragment's contract.

However, there is something in your code that can affect performance. Your setOnClickListener() call allocates new listener whenever onBindViewHolder() is called, which is pretty often if you scroll fast enough. It adds to the work of your garbage collector.

You can always get ViewHolder 's position in adapter with getBindingAdapterPosition() (or its deprecated counterpart getAdapterPosition() if you're using an older version of RecyclerView). As you can see from the docs, in marginal cases it may not be consistent with the position you use and is a more correct approach to handling user actions.

With that said, it is sufficient to set your listener only once, in onCreateViewHolder() :

override fun onCreateViewHolder(
    parent: ViewGroup,
    viewType: Int
): MyViewHolder {
    val view = LayoutInflater.from(parent.context)
        .inflate(R.layout.support_simple_spinner_dropdown_item, parent, false)
    val holder = MyViewHolder(view)

    holder.itemView.setOnClickListener {
        holder
            .bindingAdapterPosition
            .takeUnless { it == RecyclerView.NO_POSITION }
            ?.let { position ->
                onItemClickListener.onClick(data[position])
            }
    }

    return holder
}

Here you can use Callback , it is the recommended choice of Google's Android team. You implement that as:


class MyFragment : Fragment(){
override fun onCreateView(
    inflater: LayoutInflater, container: ViewGroup?,
    savedInstanceState: Bundle?
    ): View? {
        // Inflate the layout for this fragment
        myRecyclerView.adapter = MyAdapter(it) { item ->

           // You can use your item here.

        }
        return view
    }

class MyAdapter(val data: List<MyItem> ,
               private val callback: (MyItem) -> Unit) :
    RecyclerView.Adapter<MyViewHolder>() {
    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): MyViewHolder {
        val view = LayoutInflater.from(parent.context)
            .inflate(R.layout.support_simple_spinner_dropdown_item, parent, false)
        return MyViewHolder(view)
    }

    override fun getItemCount() = data.size

    override fun onBindViewHolder(holder: MyViewHolder, position: Int) {
        holder.itemView.setOnClickListener{
            callback.invoke(data[position])
        }
        with(holder) {
            textView1?.let {
                it.text = data[position].Name
            }
        }
    }

}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM