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