I have seen many examples for providing click functionality (for selecting row item in recycler view) to a row item, but all of them are using custom click listener class to achieve this. Is there any method to achieve this click functionality?
问题:
回答1:
Detecting click on a RecyclerView item is very easy. All you need to do is define an interface (if you're not using Kotlin, in which case you just pass in a lambda):
public class MyAdapter extends RecyclerView.Adapter<MyViewHolder> {
private final Clicks clicks;
public MyAdapter(Clicks clicks) {
this.clicks = clicks;
}
private List<MyObject> items = Collections.emptyList();
public void updateData(List<MyObject> items) {
this.items = items;
notifyDataSetChanged(); // TODO: use ListAdapter for diffing instead if you need animations
}
public interface Clicks {
void onItemSelected(MyObject myObject, int position);
}
public class MyViewHolder extends RecyclerView.ViewHolder {
private MyObject myObject;
public MyViewHolder(View view) {
super(view);
// bind views
view.setOnClickListener((v) -> {
int adapterPosition = getAdapterPosition();
if(adapterPosition >= 0) {
clicks.onItemSelected(myObject, adapterPosition);
}
});
}
public void bind(MyObject myObject) {
this.myObject = myObject;
// bind data to views
}
}
}
Same code in Kotlin:
class MyAdapter(val itemClicks: (MyObject, Int) -> Unit): RecyclerView.Adapter<MyViewHolder>() {
private var items: List<MyObject> = Collections.emptyList()
fun updateData(items: List<MyObject>) {
this.items = items
notifyDataSetChanged() // TODO: use ListAdapter for diffing instead if you need animations
}
inner class MyViewHolder(val myView: View): RecyclerView.ViewHolder(myView) {
private lateinit var myObject: MyObject
init {
// binds views
myView.onClick {
val adapterPosition = getAdapterPosition()
if(adapterPosition >= 0) {
itemClicks.invoke(myObject, adapterPosition)
}
}
}
fun bind(myObject: MyObject) {
this.myObject = myObject
// bind data to views
}
}
}
回答2:
If you are using Kotlin then you can send method as constructor parameter or via setter method. You don't need to created any interface listener for this kind of situation in kotlin.
class ImageCategoryAdapter(val listener: (YourType) -> Unit) : RecyclerView.Adapter<ImageCategoryAdapter.ViewHolder>() {
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
return ViewHolder(LayoutInflater.from(parent.context).inflate(R.layout.item_poll_category, parent, false))
}
override fun getItemCount() = yourList.size
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
// ...
}
inner class ViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
// ...
init {
// this is the magic
itemView.setOnClickListener { listener.invoke(yourList[adapterPosition]) }
}
}
}
In adapter we are taking function as constructor parameter named listener. and then on click of item view we are invoking that function with required type.
val adapter = ImageCategoryAdapter {
// Your callback
// here it will be type of parameter which we are sending from adapter by invoking the function.
processClickedItem(it)
}
This is why i am getting addicted to kotlin. No interface callback needed for this kind of situation.
Updated If you want to send the position also.
class ImageCategoryAdapter(val listener: (YourType, Int) -> Unit) : RecyclerView.Adapter<ImageCategoryAdapter.ViewHolder>() {
...
}
You have to invoke this function with two parameter.
itemView.setOnClickListener { listener.invoke(yourList[adapterPosition], adapterPosition) }
Now as we can see there are multiple parameter so it
will not work for parameter, we have to provide the variable name for this lambda function.
val adapter = ImageCategoryAdapter { clickItem, position -> processClickedItem(clickItem, position)}