25

This is my first attempt at implementing the RecyclerView. I have implemented a Callback interface between the Adapter and the ViewHolder Class to handle Click Events on the UI elements (buttons). I am having problems getting a reference to the ViewHolder that the click occurred in. I can click the Button for the first item in the list, however the action that i'd defined is performed on the last viewholder in my recycler view. I finally figured out to pass the position through my interface, but I'm struggling to understand how to get a reference to that viewholder` from it.

    public class RoomAdapter extends RecyclerView.Adapter<RoomAdapter.RoomViewHolder>
{
    List<Room> mRooms;
    public RoomAdapter(List<Room> rooms) {
        mRooms = rooms;
    }

    @Override
    public RoomViewHolder onCreateViewHolder(ViewGroup viewGroup, int i) {
        final View viewItem = LayoutInflater
                .from(viewGroup.getContext())
                .inflate(R.layout.room_rv_item,viewGroup,false);
        return vh = new RoomViewHolder(viewItem,new IRoomViewClick() {
            @Override
            public void editname(int pos) {

            }
        });
    }

    @Override
    public void onBindViewHolder(RoomViewHolder roomViewHolder, int i) {
        Room r = getItem(i);
        roomViewHolder.label.setText(r.name);


    }
    public Room getItem(int position)
    {
        return mRooms.get(position);
    }
    @Override
    public long getItemId(int position) {
        return mRooms.get(position).getId();
    }

    @Override
    public int getItemCount() {
        return mRooms.size();
    }

    public static class RoomViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener
    {
        TextView label;
        Button editBtn;
        LinearLayout labelView;
        Button saveBtn;
        Button deleteBtn;
        EditText editNameET;
        IRoomViewClick mListener;
        public RoomViewHolder(View itemView, IRoomViewClick listener) {
            super(itemView);
            labelView = (LinearLayout) itemView.findViewById(R.id.labelview);
            mListener = listener;
            editBtn =(Button) itemView.findViewById(R.id.editbtn);
            editBtn.setOnClickListener(this);
            label = (TextView) itemView.findViewById(R.id.room_listitem_label);

        }

        @Override
        public void onClick(View view) {
            int pos = getPosition();
            mListener.editname(pos);



        }
    }
    public static interface IRoomViewClick
    {
        public void editname(int pos);
    }

}

enter image description here

Vasily Kabunov
  • 6,511
  • 13
  • 49
  • 53
gsueagle2008
  • 4,583
  • 10
  • 36
  • 46
  • Would that not just get me a ref to the button instead of the ViewHolder itself? – gsueagle2008 Nov 12 '14 at 14:37
  • heh ... i didn't read the question carefully ... i thought that you need a position (since i did not see getPosition()'s body in your code) ... but yes, you can put there ViewHolder instead ... but then you will not know the position, since ... well, it is RecyclerView and view/viewholder can be ... recycled ... – Selvin Nov 12 '14 at 14:41
  • Yeah I missed implementing the getPosition() function in my code. That probably did not help. I'm still not completely clear on setting and getting tags, and converting that into a ViewHolder that I can manipulate. – gsueagle2008 Nov 12 '14 at 15:55
  • Tag is an object.. You can put there any object but after getting it you have to cast it to proper class – Selvin Nov 12 '14 at 15:56
  • I understand that, at what point in the lifecycle do I set and get the tags, and what object do i tag and what do I tag it with? – gsueagle2008 Nov 12 '14 at 16:31
  • I think I figured it out. The Solution was to tag the roomholder itemview with the roomholder object in the onBindViewHolder event. – gsueagle2008 Nov 12 '14 at 17:07
  • Why are you keeping a reference to the last ViewHolder you've created? (`RoomViewHolder vh;`) – yigit Nov 12 '14 at 20:11
  • it's just cruft from trying different things. – gsueagle2008 Nov 12 '14 at 20:40

4 Answers4

25

you can set a tag for the editBtn in onBindViewHolder:

    public RoomViewHolder(View itemView, IRoomViewClick listener) {
        ....
        editBtn =(Button) itemView.findViewById(R.id.editbtn);
        //add tag for this view
        editBtn.setTag(this);
        ....
   }

and get it in onClick

    @Override
    public void onClick(View view) {
        //getTag
        RoomViewHolder holder = (RoomViewHolder )(view.getTag());
        int pos = getPosition();
        mListener.editname(pos);
    ...
    }
Juude
  • 1,207
  • 2
  • 13
  • 22
  • 13
    Unbelievable that Google did not think of better way of doing it - almost year passed and there is no better way to get ViewHolder that via old setTag method (that was part of view holder unofficial pattern)... – Nuwisam Nov 21 '15 at 14:37
13

Actually you can do better than getTag by calling getChildViewHolder(View child) on the RecyclerView:

@Override
public void onClick(View v) {
    MyHolder holder = (MyHolder) mRecyclerView.getChildViewHolder(v);
    holder.textView.setText("Clicked!");
}
Shawn Lauzon
  • 6,234
  • 4
  • 35
  • 46
  • it will give all views in the item can be accessed or only clicked item? https://github.com/CymChad/BaseRecyclerViewAdapterHelper/issues/594 – LOG_TAG Dec 06 '16 at 06:42
  • When the view is clicked, this function will access that view which is clicked. But other than it being in the `onClick` handler, it can get views which are clicked or not. – Shawn Lauzon Dec 07 '16 at 00:55
  • thanks, I'm thinking getting bitmap form recyclerview displayed imageviews drawable to share via whatsapp !! getting the image fromcache working fine but it's very slow (Using fresco )!! – LOG_TAG Dec 07 '16 at 02:41
  • Yeah if you're getting from cache you're accessing the db. If you have some small images, better to keep them around. – Shawn Lauzon Dec 07 '16 at 03:01
6
@Override
public void onClick(View v) {
    int pos = getAdapterPosition(); //getPosition() is deprecated 
    notifyItemChanged(pos);
}

notifyItemChanged will notify your onBindViewHolder, then you can do something like this

public void onBindViewHolder(RoomViewHolder roomViewHolder, int i) {
    Room r = getItem(i);
    roomViewHolder.label.setText(r.name);
    if(i == pos){ // Do Something }

}

Whenever you click the item, it will change pos' value and trigger the // Do Something

Jules Lee
  • 83
  • 2
  • 7
2

Turn out that RecycleView saves the view position in the view layout params, so if you just want the view position from to get the right item from the list you can use this:

RecyclerView.LayoutParams lp = (RecyclerView.LayoutParams) v.getLayoutParams();
lp.getViewPosition();
shem
  • 4,686
  • 2
  • 32
  • 43
  • 1
    `getViewPosition` is deprecated, you should now use `getViewLayoutPosition` or `getViewAdapterPosition`, usually the latter. – androidguy Apr 18 '17 at 03:18