2

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?

ClassA
  • 2,480
  • 1
  • 26
  • 57

2 Answers2

2

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.

ClassA
  • 2,480
  • 1
  • 26
  • 57
0

In your ViewHolder constructor do something like this

class ViewHolder extends android.support.v7.widget.RecyclerView.ViewHolder {
    TextView name;
    CircularImageView image;
    ImageButton viewholderOtions;

    ViewHolder(View itemView) {
        super(itemView);
        // initialize your view here and set click listener 
        name = (TextView) itemView.findViewById(R.id.name);
        name.setOnClickListener(new View.OnClickListener() {
          @Override
          public void onClick(View v) {
           // do your stuffs

          }
        });
    }
    public void setText(String text){
      // do your thing
    }
}

Now in your onBindViewHolder

   @Override
public void onBindViewHolder(ViewHolder holder, int position) {
    holder.setText("The text you want to pass to view holder");
}

And also I don't think its good idea to use global myLayoutView. It will mess up with your view. Instead use viewHolder.itemview

umuieme
  • 729
  • 6
  • 20
  • thank you for answering, how will I then get text from my holder inside `onBindViewHolder ` to `ViewHolder(View itemView)`? – ClassA Aug 07 '17 at 13:15
  • as well as the position of the selected `ViewHolder` – ClassA Aug 07 '17 at 13:21
  • i didn't get what you trying to say. Did you mean you want to pass text to your `viewHolder` from `onBindViewhHolder`? If yes then create a method in your `ViewHolder` something like `setText(String text)` and call it from `onBindViewHolder` like this `viewHolder.setText("your text")`. Same goes for the position. – umuieme Aug 07 '17 at 13:24
  • Can you please update your answer and show me what you mean? I only need this then your answer is the accepted answer – ClassA Aug 07 '17 at 14:04