Get clicked item and its position in RecyclerView
Asked Answered
H

22

125

I am replacing my ListView with RecyclerView, list showing ok, but I would like to know how to get clicked item and its position, similar to the method OnItemClickListener.onItemClick(AdapterView parent, View v, int position, long id) we use in ListView.

Thanks for ideas!

Hooknose answered 3/2, 2015 at 10:32 Comment(3)
check this tutorial wiki.workassis.com/android-recyclerview-exampleRampant
easiest way as for me: CustomSelectionCallbackWooldridge
use absoluteAdapterPosition in itemView.setOnClickListener {}Neuman
H
194

Based on the link: Why doesn't RecyclerView have onItemClickListener()? and How RecyclerView is different from Listview?, and also @Duncan's general idea, I give my solution here:

  • Define one interface RecyclerViewClickListener for a passing message from the adapter to Activity/Fragment:

      public interface RecyclerViewClickListener {
          public void recyclerViewListClicked(View v, int position);
      }
    
  • In Activity/Fragment implement the interface, and also pass listener to adapter:

      @Override
      public void recyclerViewListClicked(View v, int position){... ...}
    
      //set up adapter and pass clicked listener this
      myAdapter = new MyRecyclerViewAdapter(context, this);
    
  • In Adapter and ViewHolder:

      public class MyRecyclerViewAdapter extends RecyclerView.Adapter<MyRecyclerViewAdapter.ItemViewHolder> {
         ... ... 
         private Context context;
         private static RecyclerViewClickListener itemListener;
    
    
         public MyRecyclerViewAdapter(Context context, RecyclerViewClickListener itemListener) {
             this.context = context;
             this.itemListener = itemListener;
             ... ...
         }
    
    
         //ViewHolder class implement OnClickListener, 
         //set clicklistener to itemView and, 
         //send message back to Activity/Fragment 
         public static class ItemViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener{
             ... ...
             public ItemViewHolder(View convertView) {
                 super(convertView);
                 ... ...
                 convertView.setOnClickListener(this);
             }
    
             @Override
             public void onClick(View v) {
                 itemListener.recyclerViewListClicked(v, this.getPosition());     
    
             }
         }
      }
    

After testing, it works fine.

[UPDATE]

Since API 22, RecyclerView.ViewHolder.getPosition() is deprecated, so instead with getLayoutPosition().

Hooknose answered 3/2, 2015 at 16:58 Comment(12)
use getLayoutPosition() method as getPosition() is depricated now.Shallow
thanks for this piece of code :) just one tip: reference the static private static RecyclerViewClickListener itemListener; in a static way in the constructor.Dextral
thank you for your help. how do we add swipe on the cards?Toxoplasmosis
What if I don't want RecyclerViewClickListenerto be static? For example, I have 3 tabs that use the same kind of adapter and the three implement RecyclerViewClickListener. But if it is not static, how to use it inside static class ItemViewHolde ?Zacharia
@Ferran Negre this is exactly what i am looking for. Have you got the answer?Bread
@khurramengr I ended up adding a listener into each item in the method onBindViewHolder. Moreover, my list item was not all clickable (only part of the item).Zacharia
If I want to use this Listener approach to delete items from the dataset and view. It works for the first item, but then the index is shifted by +1 every subsequent delete. I tried to use adapter.notifyItemRemoved(pos) but it doesn't helpCloset
how to get the clicked item object to activity from adapter android.i need more clearity am doing as per u r instruction item click listener is not calledPindus
Should you not be using a WeakReference here: new MyRecyclerViewAdapter(context, this)? This might cause memory leaksAbduct
public void recyclerViewListClicked(View v, int position); The modifier public is redundant for interface methodsCleave
@Xcihnegn: can the "this.getPosition()" be replaced with "this.getAdapterPosition()"?Zouave
On onclick inside ItemViewHolderclass, we are passing itemListener.recyclerViewListClicked(v, this.getPosition());, instead you can pass recyclerviewItemClickListener.OnItemClick(view,getAdapterPosition());this as well.Causeway
A
39
public class MyRvAdapter extends RecyclerView.Adapter<MyRvAdapter.MyViewHolder>{
    public Context context;
    public ArrayList<RvDataItem> dataItems;

    ...
    constructor
    overrides
    ...

    class MyViewHolder extends RecyclerView.ViewHolder{
        public TextView textView;
        public Context context;

        public MyViewHolder(View itemView, Context context) {
            super(itemView);

            this.context = context;

            this.textView = (TextView)itemView.findViewById(R.id.textView);

            // on item click
            itemView.setOnClickListener(new View.OnClickListener(){
                @Override
                public void onClick(View v) {
                    // get position
                    int pos = getAdapterPosition();

                    // check if item still exists
                    if(pos != RecyclerView.NO_POSITION){
                        RvDataItem clickedDataItem = dataItems.get(pos);
                        Toast.makeText(v.getContext(), "You clicked " + clickedDataItem.getName(), Toast.LENGTH_SHORT).show();
                    }
                }
            });
        }
    }
}
Aleksandrovsk answered 26/9, 2016 at 16:19 Comment(2)
Thank you. getAdapterPosition() is what I needed.Adamson
pos != RecyclerView.NO_POSITION might fail. eg: pos = -2. pos > RecyclerView.NO_POSITION is better..I think ?Finer
B
13

Here is an Example to set a Click Listener.

Adapter extends  RecyclerView.Adapter<MessageAdapter.MessageViewHolder> {  ...  }

 public static class MessageViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener {
        public TextView     tv_msg;
        public TextView     tv_date;
        public TextView     tv_sendTime;
        public ImageView    sharedFile;
        public ProgressBar sendingProgressBar;

        public MessageViewHolder(View v) {
            super(v);
            tv_msg =  (TextView) v.findViewById(R.id.tv_msg);
            tv_date = (TextView)  v.findViewById(R.id.tv_date);
            tv_sendTime = (TextView)  v.findViewById(R.id.tv_sendTime);
            sendingProgressBar = (ProgressBar) v.findViewById(R.id.sendingProgressBar);
            sharedFile = (ImageView) v.findViewById(R.id.sharedFile);

            sharedFile.setOnClickListener(this);

        }

        @Override
        public void onClick(View view) {
        int position  =   getAdapterPosition();


            switch (view.getId()){
                case R.id.sharedFile:


                    Log.w("", "Selected"+position);


                    break;
            }
        }

    }
Busey answered 29/7, 2015 at 22:19 Comment(6)
What is getAdapterPostion(); here?Blane
the position of the view clicked. is not a custom method.Busey
This seems like the cleanest way to do this, especially since you're almost always interested in getting data using the position and may not have access to the RecyclerView object.Brevier
Thanks a lot for int position = getAdapterPosition();Citral
Please take note that calling getAdapterPosition while the list is being updated, e.g. because of calling notififyXYZ, will return -1Herrle
@david read this: #29684654Busey
A
11

Put this code where you define recycler view in activity.

    rv_list.addOnItemTouchListener(
            new RecyclerItemClickListener(activity, new RecyclerItemClickListener.OnItemClickListener() {
                @Override
                public void onItemClick(View v, int position) {

                    Toast.makeText(activity, "" + position, Toast.LENGTH_SHORT).show();
                }
            })
    );

Then make separate class and put this code:

import android.content.Context;
import android.support.v7.widget.RecyclerView;
import android.view.GestureDetector;
import android.view.MotionEvent;
import android.view.View;
public class RecyclerItemClickListener implements RecyclerView.OnItemTouchListener {

    private OnItemClickListener mListener;
    public interface OnItemClickListener {
        public void onItemClick(View view, int position);
    }
    GestureDetector mGestureDetector;
    public RecyclerItemClickListener(Context context, OnItemClickListener listener) {
        mListener = listener;
        mGestureDetector = new GestureDetector(context, new GestureDetector.SimpleOnGestureListener() {
            @Override
            public boolean onSingleTapUp(MotionEvent e) {
                return true;
            }
        });
    }
    @Override
    public boolean onInterceptTouchEvent(RecyclerView view, MotionEvent e) {
        View childView = view.findChildViewUnder(e.getX(), e.getY());
        if (childView != null && mListener != null && mGestureDetector.onTouchEvent(e)) {
            mListener.onItemClick(childView, view.getChildAdapterPosition(childView));
        }
        return false;
    }

    @Override
    public void onTouchEvent(RecyclerView view, MotionEvent motionEvent) {
    }

    @Override
    public void onRequestDisallowInterceptTouchEvent(boolean disallowIntercept) {

    }
}
Arguable answered 18/5, 2016 at 8:30 Comment(0)
T
7

create java file with below code

public class RecyclerItemClickListener implements RecyclerView.OnItemTouchListener {
private OnItemClickListener mListener;

public interface OnItemClickListener {
    public void onItemClick(View view, int position);
}

GestureDetector mGestureDetector;

public RecyclerItemClickListener(Context context, OnItemClickListener listener) {
    mListener = listener;
    mGestureDetector = new GestureDetector(context, new GestureDetector.SimpleOnGestureListener() {
        @Override public boolean onSingleTapUp(MotionEvent e) {
            return true;
        }
    });
}

@Override public boolean onInterceptTouchEvent(RecyclerView view, MotionEvent e) {
    View childView = view.findChildViewUnder(e.getX(), e.getY());
    if (childView != null && mListener != null && mGestureDetector.onTouchEvent(e)) {
        mListener.onItemClick(childView, view.getChildLayoutPosition(childView));
        return true;
    }
    return false;
}

@Override public void onTouchEvent(RecyclerView view, MotionEvent motionEvent) { }

@Override
public void onRequestDisallowInterceptTouchEvent(boolean disallowIntercept) {

}

and just use the listener on your RecyclerView object.

recyclerView.addOnItemTouchListener(  
new RecyclerItemClickListener(context, new RecyclerItemClickListener.OnItemClickListener() {
  @Override public void onItemClick(View view, int position) {
    // TODO Handle item click
  }
}));
Trimeter answered 28/9, 2018 at 12:22 Comment(1)
RecyclerItemClickListener is not a default android class, you should provide the library you are using or the class itself.Melia
O
5

If you want Click event of recycle-View from activity/fragment instead of adapter then you can also use following short cut way.

recyclerView.setOnTouchListener(new View.OnTouchListener() {
            @Override
            public boolean onTouch(View v, MotionEvent event) {
                final TextView txtStatusChange = (TextView)v.findViewById(R.id.txt_key_status);
                txtStatusChange.setOnClickListener(new View.OnClickListener() {
                    @Override
                    public void onClick(View v) {
                        Log.e(TAG, "hello text " + txtStatusChange.getText().toString() + " TAG " + txtStatusChange.getTag().toString());
                        Util.showToast(CampaignLiveActivity.this,"hello");
                    }
                });
                return false;
            }
        });

You can also use other long ways like using interface

Oddment answered 20/8, 2015 at 12:1 Comment(3)
,, hi there, but what if we have to get position of item clicked as well. ?Bailes
You can just set the position of item in TAG through adapter and then get the tag as mentioned above txtStatusChange.getTag().toString()Oddment
@AmandeepRohila In order for the code to work, I have to scroll to the end of a horizontal recyclerview first. Only then does the toast work. Isn't there a way to make it work without having to scroll forth and back first?Kolnos
L
4
recyclerViewObject.addOnItemTouchListener(
    new RecyclerItemClickListener(
        getContext(),
        recyclerViewObject,
        new RecyclerItemClickListener.OnItemClickListener() {
            @Override public void onItemClick(View view, int position) {
                // view is the clicked view (the one you wanted
                // position is its position in the adapter
            }
            @Override public void onLongItemClick(View view, int position) {
            }
        }
    )
);
Labor answered 25/11, 2016 at 11:9 Comment(1)
this is not working since RecyclerItemClickListener doesn't exist (anymore).Trod
E
3

Use below code:-

public class SergejAdapter extends RecyclerView.Adapter<SergejAdapter.MyViewHolder>{

...

class MyViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener{


    @Override
    public void onClick(View v) {
        // here you use position
        int position = getAdapterPosition();
        ...

    }
}
}
Encephalograph answered 6/7, 2016 at 11:33 Comment(0)
C
3

Here is the simplest and the easiest way to find the position of the clicked item:

I've also faced the same problem.

I wanted to find of the position of the clicked/selected item of the RecyclerView() and perform some specific operations on that particular item.

getAdapterPosition() method works like a charm for these kind of stuff. I found this method after a day of long research and after trying numerous other methods.

int position = getAdapterPosition();
Toast.makeText(this, "Position is: "+position, Toast.LENGTH_SHORT).show();

You do not have to use any extra method. Just create a global variable named 'position' and initialize it with getAdapterPosition() in any of the major method of the adapter (class or similar).

Here is a brief documentation from this link.

getAdapterPosition added in version 22.1.0 int getAdapterPosition () Returns the Adapter position of the item represented by this ViewHolder. Note that this might be different than the getLayoutPosition() if there are pending adapter updates but a new layout pass has not happened yet. RecyclerView does not handle any adapter updates until the next layout traversal. This may create temporary inconsistencies between what user sees on the screen and what adapter contents have. This inconsistency is not important since it will be less than 16ms but it might be a problem if you want to use ViewHolder position to access the adapter. Sometimes, you may need to get the exact adapter position to do some actions in response to user events. In that case, you should use this method which will calculate the Adapter position of the ViewHolder.

Happy to help. Feel free to ask doubts.

Cristinacristine answered 10/8, 2018 at 21:4 Comment(1)
what is simplest and easiest way to get value of clicked item of RecyclerView?Halley
M
2

My simple solution

Make a position holder:

    public class PositionHolder {

    private int position;

    public PositionHolder(int position) {
        this.position = position;
    }

    public int getPosition() {
        return position;
    }

    public void setPosition(int position) {
        this.position = position;
    }
}

Just position or put data you need to get from activity.

Adapter constructor:

public ItemsAdapter(Context context, List<Item> items, PositionHolder positionHolder){
        this.context = context;
        this.items = items;
        this.positionHolder = positionHolder;
}

In Activity:

 @Override
    protected void onCreate(Bundle savedInstanceState) {

        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        selectedPosition = 0;

        positionHolder = new PositionHolder(selectedPosition);
        initView();
    }

In Adapter onClickLictener in the item
in onBindViewHolder

holder.holderButton.setOnClickListener(v -> {
            positionHolder.setPosition(position);
            notifyDataSetChanged();
        });

Now whenever you change position in RecyclerView it is hold in the Holder (or maybe it should be called Listener)

I hope it will be usefull

My first post ;P

Malaysia answered 17/7, 2020 at 16:22 Comment(0)
H
1

RecyclerView doesn't provide such method.

To manage click events on RecyclerView I ended up implementing onClickListener in my adapter, when binding the ViewHolder: In my ViewHolder I keep a reference to the root view (as you can do with your ImageViews, TextViews, etc...) and when binding the viewHolder I set a tag on it with information I need to handle click (such as position) and a clicklistener

Hydantoin answered 3/2, 2015 at 10:43 Comment(1)
yes we can set click listener in ViewHolder, but I need pass back the clicked item view and position to my fragment for further processHooknose
J
1

Everytime I use another approach. People seem to store or get position on a view, rather than storing a reference to an object that is displayed by ViewHolder.

I use this approach instead, and just store it in ViewHolder when onBindViewHolder() is called, and set reference to null in onViewRecycled().

Every time ViewHolder becomes invisible, it's recycled. So this doesn't affect in large memory consumption.

@Override
public void onBindViewHolder(final ItemViewHolder holder, int position) {
    ...
    holder.displayedItem = adapterItemsList.get(i);
    ...
}

@Override
public void onViewRecycled(ItemViewHolder holder) {
    ...
    holder.displayedItem = null;
    ...
}

class ItemViewHolder extends RecyclerView.ViewHolder {
    ...
    MySuperItemObject displayedItem = null;
    ...
}
Jeramyjerba answered 19/10, 2017 at 22:34 Comment(0)
B
1

From the designer, you can set the onClick property of the listItem to a method defined with a single parameter. I have an example method defined below. The method getAdapterPosition will give you the index of the selected listItem.

public void exampleOnClickMethod(View view){
    myRecyclerView.getChildViewHolder(view).getAdapterPosition());
}

For information on setting up a RecyclerView, see the documentation here: https://developer.android.com/guide/topics/ui/layout/recyclerview

Bledsoe answered 19/9, 2018 at 5:3 Comment(0)
D
1

The simple (but not so obvious) solution is to do this:

@Override
    public ViewHolder onCreateViewHolder(ViewGroup viewGroup, int viewType) {

        View v = LayoutInflater.from(viewGroup.getContext())
                .inflate(R.layout.recycler_row, viewGroup, false);
        ViewHolder vh = new ViewHolder(v);

and then, whenever, call the method currentPosition = vh.getLayoutPosition();

In my case, I do that in an onClick listener put on that vh View. IMHO, the recycleView class misses out in that .getPosition() and other features that we know from ListView, and that are sometimes mandatory, are simply not available. I strongly regret having moved from ListView to Recycle ditto. The timethief cost was more than a day to unveil its mysteries. Bad engineering. (But what is there is ok)

Doubletongue answered 21/4, 2021 at 19:48 Comment(1)
After wasting an entire day searching for a solution, I stumbled across this which was exactly what I needed! Works perfectly.Phrixus
S
0
//Create below methods into the Activity which contains RecyclerView.


private void createRecyclerView() {

final RecyclerView recyclerView = (RecyclerView)findViewById(R.id.recyclerView);
    recyclerView.setLayoutManager(new LinearLayoutManager(this));
    MyAdapter myAdapter=new MyAdapter(dataAray,MianActivity.this);
    recyclerView.setAdapter(myAdapter);
    recyclerView.setItemAnimator(new DefaultItemAnimator());

    setRecyclerViewClickListner(recyclerView);
}

private void setRecyclerViewClickListner(RecyclerView recyclerView){

    final GestureDetector gestureDetector = new GestureDetector(MainActivity.this,new GestureDetector.SimpleOnGestureListener() {
        @Override public boolean onSingleTapUp(MotionEvent e) {
            return true;
        }
    });


    recyclerView.addOnItemTouchListener(new RecyclerView.OnItemTouchListener() {
        @Override
        public boolean onInterceptTouchEvent(RecyclerView recyclerView, MotionEvent motionEvent) {
            View child =recyclerView.findChildViewUnder(motionEvent.getX(),motionEvent.getY());
            if(child!=null && gestureDetector.onTouchEvent(motionEvent)){
               int position=recyclerView.getChildLayoutPosition(child);
                String name=itemArray.get(position).name;

            return true;
        }

        @Override
        public void onTouchEvent(RecyclerView recyclerView, MotionEvent motionEvent) {

        }

        @Override
        public void onRequestDisallowInterceptTouchEvent(boolean b) {

        }
    });
}
Slovene answered 16/8, 2015 at 7:55 Comment(1)
Please add an explanation, just dumping code without introduction and an explanation how this fixes the problem is not very helpful.Madelinemadella
O
0

Try in this way

Adapter class :

 public class ContentAdapter extends RecyclerView.Adapter<ContentAdapter.ViewHolder> {

public interface OnItemClickListener {
    void onItemClick(ContentItem item);
}

private final List<ContentItem> items;
private final OnItemClickListener listener;

public ContentAdapter(List<ContentItem> items, OnItemClickListener listener) {
    this.items = items;
    this.listener = listener;
}

@Override public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
    View v = LayoutInflater.from(parent.getContext()).inflate(R.layout.view_item, parent, false);
    return new ViewHolder(v);
}

@Override public void onBindViewHolder(ViewHolder holder, int position) {
    holder.bind(items.get(position), listener);
}

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

static class ViewHolder extends RecyclerView.ViewHolder {

    private TextView name;
    private ImageView image;

    public ViewHolder(View itemView) {
        super(itemView);
        name = (TextView) itemView.findViewById(R.id.name);
        image = (ImageView) itemView.findViewById(R.id.image);
    }

    public void bind(final ContentItem item, final OnItemClickListener listener) {
        name.setText(item.name);
        itemView.setOnClickListener(new View.OnClickListener() {
            @Override public void onClick(View v) {
                listener.onItemClick(item);
            }
        });
    }
}

}

In your Activity or fragment :

ContentAdapter adapter = new ContentAdapter(itemList, this);

Note : Implement the OnItemClickListener based on context given by you in activity or fragment and overide methods.

Ornamented answered 31/8, 2016 at 8:31 Comment(1)
Nice solution above. Can you add to the above to show what the onItemClick method should look like in the Activity? I am trying to pass the item's position to an Intent (to open the next Activity) and I keep getting the wrong position.Zouave
F
0

Short extension for Kotlin
Method returns absolute position of all items (not the position of only visible items).

fun RecyclerView.getChildPositionAt(x: Float, y: Float): Int {
    return getChildAdapterPosition(findChildViewUnder(x, y))
}

And usage

val position = recyclerView.getChildPositionAt(event.x, event.y)
Finedrawn answered 4/7, 2018 at 23:16 Comment(0)
M
0

Use getLayoutPosition() in your custom interface java method. This will return the selected position of an item, check full detail on

https://becody.com/get-clicked-item-and-its-position-in-recyclerview/

Mounting answered 25/8, 2018 at 21:14 Comment(1)
Welcome to Stack Overflow! While links are great way of sharing knowledge, they won't really answer the question if they get broken in the future. Add to your answer the essential content of the link which answers the question. In case the content is too complex or too big to fit here, describe the general idea of the proposed solution. Remember to always keep a link reference to the original solution's website. See: How do I write a good answer?Flournoy
R
0
//simply check if the adapter position you get not less than zero

holder.btnDelItem.setOnClickListener(new View.OnClickListener() {
    @Override
    public void onClick(View v) {
        if(holder.getAdapterPosition()>=0){
            list.remove(holder.getAdapterPosition());
            notifyDataSetChanged();
        }
    }
});
Raper answered 28/11, 2019 at 10:35 Comment(1)
When answering an old question, your answer would be much more useful to other StackOverflow users if you included some context to explain how your answer helps, particularly for a question that already has an accepted answer. See: How do I write a good answer.Iatric
S
0

After lot of trial and error I found that there is a very easy way to do this that is by creating an interface in adapter class and implementing that in fragment now here comes the twist, I instansiated the view model inside my override function present In the fragment now you can send the data from that function to viemodel and from there anywhere.

I don't know if this method is good coding method but please let me know in comment and if any wants to see the let me know in the comment section I regularly open stackover flow.

Scalpel answered 15/4, 2020 at 8:40 Comment(1)
Sounds good but can you please provide more detailsAllelomorph
B
0
recyclerView.addOnItemTouchListener(object : AdapterView.OnItemClickListener,
                RecyclerView.OnItemTouchListener {
                override fun onItemClick(p0: AdapterView<*>?, p1: View?, p2: Int, p3: Long) {
                    TODO("Not yet implemented")
                }

                override fun onTouchEvent(rv: RecyclerView, e: MotionEvent) {
                    TODO("Not yet implemented")
                }

                override fun onInterceptTouchEvent(rv: RecyclerView, e: MotionEvent): Boolean {
                    TODO("Not yet implemented")
                }

                override fun onRequestDisallowInterceptTouchEvent(disallowIntercept: Boolean) {
                    TODO("Not yet implemented")
                }

            })

If use Kotlin

Bunkhouse answered 28/6, 2020 at 7:1 Comment(1)
While this code may solve the question, including an explanation of how and why this solves the problem would really help to improve the quality of your post, and probably result in more up-votes. Remember that you are answering the question for readers in the future, not just the person asking now. Please edit your answer to add explanations and give an indication of what limitations and assumptions apply.Redvers
G
-4

In onViewHolder set onClickListiner to any view and in side click use this code :

Toast.makeText(Drawer_bar.this, "position" + position, Toast.LENGTH_SHORT).show();

Replace Drawer_Bar with your Activity name.

Georgenegeorges answered 20/6, 2016 at 6:14 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.