Currently:
I'm implementing click events in my Adapter
inside of onBindViewHolder
, but I have noticed that the adapter positions get messed up when doing this. I done some research and found this, where he state:
As for why it is better in the ViewHolder instead of in onBindViewHolder(), that is because onBindViewHolder() is called for each and every item and setting the click listener is an unnecessary option to repeat when you can call it once in your ViewHolder constructor.
and the example he refer to looks like this:
public class MyViewHolder extends RecyclerView.ViewHolder implements View.OnItemClickListener {
public MyViewHolder(View view) {
super(view);
view.setOnClickListener(this);
}
@Override
public void OnClick(View view) {
// Get the item clicked
// For this example, I'm assuming your data source is of type `List<MyObject>`
MyObject myObject = mDataSource.get(getAdapterPosition());
// Then you can do any actions on it, for example -
myObject.setChecked();
}
}
This makes sense to me, but my issue is that I have a ImageButton
inside my ViewHolder
and his example is only showing how to handle click events for the ViewHolder
itself. Please first have a look at how my adapter currently looks like:
public class MyAdapter extends Adapter<MyAdapter.ViewHolder> {
ArrayList<Bitmap> bitmapArrayList;
Context context;
LayoutInflater layoutInflater;
View myLayoutView;
ArrayList<PathModel> swingThumbPathList;
ArrayList<PathModel> swingVideoPathList = new ArrayList();
class ViewHolder extends android.support.v7.widget.RecyclerView.ViewHolder {
TextView name;
CircularImageView image;
ImageButton viewholderOtions;
ViewHolder(View itemView) {
super(itemView);
}
}
public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
this.myLayoutView = LayoutInflater.from(this.context).inflate(R.layout.single_item, parent, false);
return new ViewHolder(this.myLayoutView);
}
public void onBindViewHolder(ViewHolder myHolder, final int position) {
final ViewHolder holder = myHolder;
holder.viewholderOtions = (ImageButton) myLayoutView.findViewById(R.id.viewholderOptions);
holder.name = (TextView) this.myLayoutView.findViewById(R.id.name);
holder.image = (CircularImageView) this.myLayoutView.findViewById(R.id.image);
holder.name.setText(Model.getPath());
holder.image.setImageURI(Uri.parse(Model.getPath()));
holder.viewholderOtions.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Toast.makeText(holder.itemView.getContext(), "Options was clicked", Toast.LENGTH_LONG).show();
showPopupMenu(holder.viewholderOtions, position);
}
});
holder.itemView.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
String uri = holder.name.getText().toString();
Toast.makeText(holder.itemView.getContext(), uri, Toast.LENGTH_LONG).show();
}
});
}
private void showPopupMenu(final View view, final int position) {
// inflate menu
PopupMenu popup = new PopupMenu(view.getContext(), view);
MenuInflater inflater = popup.getMenuInflater();
inflater.inflate(R.menu.popup_menu, popup.getMenu());
popup.setOnMenuItemClickListener(new PopupMenu.OnMenuItemClickListener() {
@Override
public boolean onMenuItemClick(MenuItem item) {
switch (item.getItemId()) {
case R.id.delete:
//The correct position is being toasted
Toast.makeText(view.getContext(), position + "Delete", Toast.LENGTH_LONG).show();
return true;
case R.id.rename:
Toast.makeText(view.getContext(), "Rename", Toast.LENGTH_LONG).show();
return true;
default:
return true;
}
}
});
popup.show();
}
My Question
How can I handle click events of the ViewHolder
itself and the ImageButton
inside of the ViewHolder
?
I had a laugh looking back at this question, so answering my own question almost 2 years later.
Using the same logic provided in the question, this is how you will go about setting click event to the view itself, as well as the elements inside the view, inside viewholder instead of onBindViewHolder
public class MyAdapter extends Adapter<MyAdapter.ViewHolder> {
ArrayList<Bitmap> bitmapArrayList;
Context context;
LayoutInflater layoutInflater;
View myLayoutView;
ArrayList<PathModel> swingThumbPathList;
ArrayList<PathModel> swingVideoPathList = new ArrayList();
class ViewHolder extends android.support.v7.widget.RecyclerView.ViewHolder {
TextView name;
CircularImageView image;
ImageButton viewholderOtions;
ViewHolder(View itemView) {
super(itemView);
viewholderOtions = itemView.findViewById(R.id.viewholderOptions);
videoName = itemView.findViewById(R.id.FilePath);
videoThumb = itemView.findViewById(R.id.VideoThumbnail);
//set OnClickListener's for the views we want click events for
itemView.setOnClickListener(this);
viewholderOtions.setOnClickListener(this);
videoThumb.setOnClickListener(this);
}
@Override
public void onClick(View v) {
//get the tag that we have set in onBindViewHolder
int position = (int) v.getTag();
if (v == viewholderOtions) {
//viewholderOtions was selected, we can use the position above to see what item, as shown below:
Log.e("viewholderOtions at position =", String.valueOf(position);
}
if (v == itemView){
//itemView was selected, we can use the position above to see what item, as shown below:
Log.e("itemView at position =", String.valueOf(position);
}
if (v == videoThumbvideoThumb){
//itemView was selected, we can use the position above to see what item, as shown below:
Log.e("videoThumbvideoThumb at position =", String.valueOf(position);
}
}
}
public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
this.myLayoutView = LayoutInflater.from(this.context).inflate(R.layout.single_item, parent, false);
return new ViewHolder(this.myLayoutView);
}
public void onBindViewHolder(ViewHolder myHolder, final int position) {
//set a tag so we can retrieve the position via the tag
myHolder.viewholderOtions.setTag(position);
//Set text to holder.name
holder.name.setText(Model.getPath());
//a better option to below is to use a library like picasso to handle the image loading
holder.image.setImageURI(Uri.parse(Model.getPath()));
}
}
But to be honest, I still set the click events inside onBindViewHolder
, then I just set the following to my RecyclerView
inside my Activity
- recyclerView.setHasFixedSize(true);
. By doing the the positions will not get messed up, as stated in my question.