Related
Has anyone using RecyclerView found a way to set an onClickListener to items in the RecyclerView?
I thought of setting a listener to each of the layouts for each item but that seems a little too much hassle
I'm sure there is a way for the RecyclerView to listen for the onClick event but I can't quite figure it out.
Here is a better and less tightly coupled way to implement an OnClickListener for a RecyclerView.
Snippet of usage:
RecyclerView recyclerView = findViewById(R.id.recycler);
recyclerView.addOnItemTouchListener(
new RecyclerItemClickListener(context, recyclerView ,new RecyclerItemClickListener.OnItemClickListener() {
#Override public void onItemClick(View view, int position) {
// do whatever
}
#Override public void onLongItemClick(View view, int position) {
// do whatever
}
})
);
RecyclerItemClickListener implementation:
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);
public void onLongItemClick(View view, int position);
}
GestureDetector mGestureDetector;
public RecyclerItemClickListener(Context context, final RecyclerView recyclerView, OnItemClickListener listener) {
mListener = listener;
mGestureDetector = new GestureDetector(context, new GestureDetector.SimpleOnGestureListener() {
#Override
public boolean onSingleTapUp(MotionEvent e) {
return true;
}
#Override
public void onLongPress(MotionEvent e) {
View child = recyclerView.findChildViewUnder(e.getX(), e.getY());
if (child != null && mListener != null) {
mListener.onLongItemClick(child, recyclerView.getChildAdapterPosition(child));
}
}
});
}
#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 true;
}
return false;
}
#Override public void onTouchEvent(RecyclerView view, MotionEvent motionEvent) { }
#Override
public void onRequestDisallowInterceptTouchEvent (boolean disallowIntercept){}
}
As the API's have radically changed, It wouldn't surprise me if you were to create an OnClickListener for each item. It isn't that much of a hassle though. In your implementation of RecyclerView.Adapter<MyViewHolder>, you should have:
private final OnClickListener mOnClickListener = new MyOnClickListener();
#Override
public MyViewHolder onCreateViewHolder(final ViewGroup parent, final int viewType) {
View view = LayoutInflater.from(mContext).inflate(R.layout.myview, parent, false);
view.setOnClickListener(mOnClickListener);
return new MyViewHolder(view);
}
The onClick method:
#Override
public void onClick(final View view) {
int itemPosition = mRecyclerView.getChildLayoutPosition(view);
String item = mList.get(itemPosition);
Toast.makeText(mContext, item, Toast.LENGTH_LONG).show();
}
I do it in this way, without undue classes, detectors etc. Simple code inside our adapter. Especially better solution for longClick than presented before.
public class PasswordAdapter extends RecyclerView.Adapter<PasswordAdapter.ViewHolder> {
private final ClickListener clickListener;
public PasswordAdapter(ClickListener clickListener) {
this.clickListener = clickListener;
}
public class ViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener, View.OnLongClickListener {
TextView name;
public ViewHolder(View itemView) {
super(itemView);
itemView.setOnClickListener(this);
itemView.setOnLongClickListener(this);
name = (TextView) itemView.findViewById(R.id.card_name);
}
#Override
public void onClick(View v) {
int position = getBindingAdapterPosition();
if (position >= 0) {
clickListener.onItemClick(position, v);
}
}
#Override
public boolean onLongClick(View v) {
int position = getBindingAdapterPosition();
if (position >= 0) {
clickListener.onItemLongClick(position, v);
return true;
}
return false;
}
}
public interface ClickListener {
void onItemClick(int position, View v);
void onItemLongClick(int position, View v);
}
}
Then inside fragment or activity, just hit:
PasswordAdapter mAdapter = new PasswordAdapter(
new PasswordAdapter.ClickListener() {
#Override
public void onItemClick(int position, View v) {
Log.d(TAG, "onItemClick position: " + position);
}
#Override
public void onItemLongClick(int position, View v) {
Log.d(TAG, "onItemLongClick pos = " + position);
}
}
);
Check out a similar question #CommonsWare's comment links to this, which implements the OnClickListener interface in the viewHolder.
Here's a simple example of the ViewHolder:
/** Declare global with in adapter class. */
TextView textView;
public static class ViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener {
private ViewHolder(View itemView) {
super(itemView);
itemView.setOnClickListener(this);
textView = (TextView) view.findViewById(android.R.id.text1);
}
#Override
public void onClick(View view) {
Toast.makeText(view.getContext(), "position = " + getLayoutPosition(), Toast.LENGTH_SHORT).show();
/** Go through each item if you have few items within RecyclerView. */
if (getLayoutPosition() == 0) {
// Do whatever you want here
} else if(getLayoutPosition() == 1) {
// Do whatever you want here
} else if(getLayoutPosition() == 2) {
// Do whatever you want here
}
/** Or you can use For loop if you have long list of items. */
for (int i = 0; i < exampleList.size(); i++) {
// Do whatever you want here
}
}
}
The creation of ViewHolder inside your RecyclerView.Adapter then looks like this:
#Override
public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
LayoutInflater inflater = LayoutInflater.from(parent.getContext())
View view = inflater.inflate(android.R.layout.simple_list_item_1, parent, false);
return new ViewHolder(view);
}
Based on Jacob Tabak's answer (+1 for him), I was able to add onLongClick listener:
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 {
public interface OnItemClickListener {
void onItemClick(View view, int position);
void onItemLongClick(View view, int position);
}
private OnItemClickListener mListener;
private GestureDetector mGestureDetector;
public RecyclerItemClickListener(Context context, final RecyclerView recyclerView, OnItemClickListener listener) {
mListener = listener;
mGestureDetector = new GestureDetector(context, new GestureDetector.SimpleOnGestureListener() {
#Override
public boolean onSingleTapUp(MotionEvent e) {
return true;
}
#Override
public void onLongPress(MotionEvent e) {
View childView = recyclerView.findChildViewUnder(e.getX(), e.getY());
if (childView != null && mListener != null) {
mListener.onItemLongClick(childView, recyclerView.getChildAdapterPosition(childView));
}
}
});
}
#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) {
}
}
Then you can use it like this:
recyclerView.addOnItemTouchListener(new RecyclerItemClickListener(getActivity(), recyclerView, new RecyclerItemClickListener.OnItemClickListener() {
#Override
public void onItemClick(View view, int position) {
// ...
}
#Override
public void onItemLongClick(View view, int position) {
// ...
}
}));
This is what worked for me. Attach the OnClickListener to the onBindView. I don't really know if this will impact the performance, but it seems to work fine with little code.
public void onBindViewHolder(ViewHolder holder, final int position) {
holder.view.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
Toast.makeText(context, "Recycle Click" + position, Toast.LENGTH_SHORT).show();
}
});
}
This was so hard for me to have on item click listener in the activity and also to have click listener for single view of the item that will not trigger on item click listener. After playing around with Jacob Tabak's answer I respect his answer for on item click if no other touch actions inside item are presented.
I have a custom OnClickListener interface that have on item click event which holds the clicked item's view and the item position from the adapter. I present an instance of it in the constructor(or it can be with setter) and attach it to the view holder container click listener.
I also have other click listener in the Adapter(Can be in the view holder) which will handle current View click from the container.
public class MyRecyclerAdapter extends RecyclerView.Adapter<MyViewHolder> {
private ArrayList<String> mData;
private OnItemClickListener mOnItemClickListener;
public interface OnItemClickListener {
public void onItemClick(View view, int position);
}
public MyRecyclerAdapter(ArrayList<String> itemsData,
OnItemClickListener onItemClickListener) {
mOnItemClickListener = onItemClickListener;
this.mData = itemsData;
}
#Override
public MyViewHolder onCreateViewHolder(ViewGroup parent,
int viewType) {
View layoutView = LayoutInflater.from(mContext).inflate(
R.layout.list_item, parent, false);
final MyViewHolder viewHolder = new MyViewHolder(layoutView);
viewHolder.container.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View v) {
mOnItemClickListener.onItemClick(v, viewHolder.getAdapterPosition());
}
});
viewHоlder.button.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View v) {
//do button click work here with
// mData.get( viewHolder.getAdapterPosition() );
}
});
return viewHolder;
}
#Override
public int getItemCount() {
return mData.size();
}}
In the activity you need to initialize the adapter by passing instance of the OnItemClickListener
public class FeedActivity extends ActionBarActivity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
...
RecyclerView recyclerView = (RecyclerView) findViewById(R.id.my_recycler_view);
.....
MyRecyclerAdapter adapter = new MyRecyclerAdapter(new ArrayList<String>(), new OnItemClickListener() {
#Override
public void onItemClick(View view, int position) {
///list item was clicked
}
});
recyclerView.setLayoutManager(new LinearLayoutManager(this));
recyclerView.setAdapter(mFeedsAdapter);
}
And my ViewHolder
public class MyViewHolder extends RecyclerView.ViewHolder {
public Button button;
public View container;
public MyViewHolder(View itemLayoutView) {
super(itemLayoutView);
container = itemLayoutView;
button = (Button) itemLayoutView.findViewById(R.id.button);
}}
This is what I ended up needing, in case someone finds it useful:
public static class ViewHolder extends RecyclerView.ViewHolder {
public ViewHolder(View item) {
super(item);
item.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View v) {
Log.d("RecyclerView", "onClick:" + getAdapterPosition());
}
});
}
}
Source: http://blog.csdn.net/jwzhangjie/article/details/36868515
I have nice solution for RecyclerView's onItemClickListener for the items and subitems
Step 1- Create an interface
public interface OnRecyclerViewItemClickListener
{
/**
* Called when any item with in recyclerview or any item with in item
* clicked
*
* #param position
* The position of the item
* #param id
* The id of the view which is clicked with in the item or
* -1 if the item itself clicked
*/
public void onRecyclerViewItemClicked(int position, int id);
}
Step 2- Then use it in adapter's onBindViewHolder method in the following way
/**
* Custom created method for Setting the item click listener for the items and items with in items
* #param listener OnRecyclerViewItemClickListener
*/
public void setOnItemClickListener(OnRecyclerViewItemClickListener listener)
{
this.listener = listener;
}
#Override
public void onBindViewHolder(ViewHolder viewHolder, final int position)
{
// viewHolder.albumBg.setBackgroundResource(_itemData[position]
// .getImageUrl());
viewHolder.albumName.setText(arrayList.get(position).getName());
viewHolder.artistName.setText(arrayList.get(position).getArtistName());
String imgUrl = arrayList.get(position).getThumbImageUrl();
makeImageRequest(imgUrl, viewHolder);
viewHolder.parentView.setOnClickListener(new View.OnClickListener()
{
#Override
public void onClick(View v)
{
listener.onRecyclerViewItemClicked(position, -1);
}
});
viewHolder.settingButton.setOnClickListener(new View.OnClickListener()
{
#Override
public void onClick(View v)
{
listener.onRecyclerViewItemClicked(position, v.getId());
}
});
}
// class to hold a reference to each item of RecyclerView
public static class ViewHolder extends RecyclerView.ViewHolder
{
public TextView albumName, artistName;
public ImageView albumIcon, settingButton;
public LinearLayout parentView;
public ViewHolder(View itemLayoutView)
{
super(itemLayoutView);
// albumBg = (LinearLayout) itemLayoutView
// .findViewById(R.id.albumDlbg);
albumName = (TextView) itemLayoutView.findViewById(R.id.albumName);
artistName = (TextView) itemLayoutView
.findViewById(R.id.artistName);
albumIcon = (ImageView) itemLayoutView.findViewById(R.id.albumIcon);
parentView = (LinearLayout) itemLayoutView
.findViewById(R.id.albumDlbg);
settingButton = (ImageView) itemLayoutView
.findViewById(R.id.settingBtn);
}
}
Step 3- find and setup recycler view in activity or fragment where you are using this
recyclerView = (RecyclerView) rootview.findViewById(R.id.vmtopsongs);
lm = new LinearLayoutManager(mActivity);
lm.setOrientation(LinearLayoutManager.VERTICAL);
recyclerView.setLayoutManager(lm);
recyclerView.addItemDecoration(
new HorizontalDividerItemDecoration.Builder(getActivity())
.paint(Utils.getPaint()).build());
PopularSongsadapter mAdapter = new PopularSongsadapter(gallery,
mActivity, true);
// set adapter
recyclerView.setAdapter(mAdapter);
mAdapter.setOnItemClickListener(this);
// set item animator to DefaultAnimator
recyclerView.setItemAnimator(new DefaultItemAnimator());
Step 4- Finally implement interface in activity or fragment where you are using the recyclerview
#Override
public void onRecyclerViewItemClicked(int position, int id)
{
if(id==-1){
Toast.makeText(mActivity, "complete item clicked", Toast.LENGTH_LONG).show();
}else{
Toast.makeText(mActivity, "setting button clicked", Toast.LENGTH_LONG).show();
}
}
Update for Kotlin Language
I have updated the code for kotlin in which only whole view has on click listener. You can set subitems click listener by editing interface and code according to above java code.
Adapter
class RecentPostsAdapter(private val list: MutableList<Post>) :
RecyclerView.Adapter<RecentPostsAdapter.ViewHolder>() {
private lateinit var onItemClickListener: OnItemClickListener
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
return ViewHolder(
LayoutInflater.from(parent.context)
.inflate(R.layout.listitem_recent_post, parent, false)
)
}
override fun getItemCount(): Int {
return list.size
}
fun setOnItemClickListener(onItemClickListener: OnItemClickListener) {
this.onItemClickListener = onItemClickListener
}
private fun getItem(position: Int): Post {
return list[position]
}
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
holder.bind(getItem(position))
holder.itemView.setOnClickListener(View.OnClickListener {
onItemClickListener.onItemClick(
position
)
})
}
class ViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
private var imageView: NetworkImageView? = null
private var tvTitle: TextView? = null
private var tvExcerpt: TextView? = null
private var htmlSpanner: HtmlSpanner = HtmlSpanner()
init {
imageView = itemView.findViewById(R.id.niv_post_image)
tvTitle = itemView.findViewById(R.id.tv_post_title)
tvExcerpt = itemView.findViewById(R.id.tv_post_excerpt)
}
fun bind(post: Post) {
tvTitle?.text = post.title
tvExcerpt?.text = htmlSpanner.fromHtml(post.excerpt)
}
}
interface OnItemClickListener {
fun onItemClick(position: Int)
}
}
Activity or Fragment
recyclerView = view.findViewById(R.id.rvHomeRecentPosts)
recyclerView.layoutManager = LinearLayoutManager(view.context)
list = mutableListOf()
recentPostsAdapter = RecentPostsAdapter(list)
recyclerView.adapter = recentPostsAdapter
recentPostsAdapter.setOnItemClickListener(object:RecentPostsAdapter.OnItemClickListener{
override fun onItemClick(position: Int) {
(activity as MainActivity).findNavController(R.id.nav_host_fragment).navigate(R.id.action_nav_home_to_nav_post_detail)
}
})
Here is what I did. This solution supports both onClick and onLongClick on both RecyclerView Items and Views insides RecyclerView Items (internal views).
I tag viewHolder on the views of my choice :
public RecyclerViewAdapter.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View itemView = LayoutInflater.from(parent.getContext()).inflate(R.layout.layout_item, null);
ViewHolder viewHolder = new ViewHolder(itemView);
itemView.setOnClickListener( this);
itemView.setOnLongClickListener(this);
viewHolder.imageIV.setOnClickListener(this);
viewHolder.imageIV.setOnLongClickListener(this);
viewHolder.imageIV.setTag(viewHolder);
itemView.setTag(viewHolder);
return viewHolder;
}
And I use holder.getPosition() to retrieve the position in onClick() method (onLongClick is similar) :
public void onClick(View view) {
ViewHolder holder = (ViewHolder) view.getTag();
int position = holder.getPosition();
if (view.getId() == holder.imageIV.getId()){
Toast.makeText(context, "imageIV onClick at" + position, Toast.LENGTH_SHORT).show();
} else {
Toast.makeText(context, "RecyclerView Item onClick at " + position, Toast.LENGTH_SHORT).show();
}
}
A variant with getChildPosition also works. Please note that for the internal views, in onClick() use :
int position = recyclerView.getChildPosition((View)view.getParent());
To my mind, the avantage of this solution is that when one clicks on the image, only the onclick() image listener is called whereas when I combined Jacob's solution for a RecyclerView Item view and my solution for internal views the RecyclerView Item view onclick() is also called (when click on image).
There is far easier way to do this. Just apply on click in onBindViewHolder on root view.
Consider this is your view for adapter,
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:card_view="http://schemas.android.com/apk/res-auto"
android:id="#+id/linearlayout"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<TextView
android:id="#+id/textview"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginBottom="1dp"
android:textSize="15sp" />
</LinearLayout>
Then do following in your adapter
//get the layout and make view holder
#Override
public RVAdapter.ViewHolder1 onCreateViewHolder(ViewGroup parent, int viewType) {
View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.adapter_layout, null);
ViewHolder1 viewHolder = new ViewHolder1(view);
return viewHolder;
}
#Override
public void onBindViewHolder(RVAdapter.ViewHolder1 holder, int position) {
//apply on click on your root view
holder.linearlayout.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
//Do on click stuff
}
});
}
//make references to views in layout including root view
public class ViewHolder1 extends RecyclerView.ViewHolder {
protected LinearLayout linearlayout = null
protected TextView textview = null;
public CareerLinksViewHolder(View itemView) {
super(itemView);
this.linearlayout = (LinearLayout) itemView.findViewById(R.id.linearlayout);
this.tvCompName = (TextView) itemView.findViewById(R.id.textview);
}
}
Way too simple and effective.
Instead of implementing interface View.OnClickListener inside view holder or creating and interface and implementing interface in your activity -
I used this code for simple on OnClickListener implementation.
public static class SimpleStringRecyclerViewAdapter
extends RecyclerView.Adapter<SimpleStringRecyclerViewAdapter.ViewHolder> {
// Your initializations goes here...
private List<String> mValues;
public static class ViewHolder extends RecyclerView.ViewHolder {
//create a variable mView
public final View mView;
/*All your row widgets goes here
public final ImageView mImageView;
public final TextView mTextView;*/
public ViewHolder(View view) {
super(view);
//Initialize it here
mView = view;
/* your row widgets initializations goes here
mImageView = (ImageView) view.findViewById(R.id.avatar);
mTextView = (TextView) view.findViewById(android.R.id.text1);*/
}
}
public String getValueAt(int position) {
return mValues.get(position);
}
public SimpleStringRecyclerViewAdapter(Context context, List<String> items) {
mBackground = mTypedValue.resourceId;
mValues = items;
}
#Override
public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View view = LayoutInflater.from(parent.getContext())
.inflate(R.layout.list_item, parent, false);
view.setBackgroundResource(mBackground);
return new ViewHolder(view);
}
#Override
public void onBindViewHolder(final ViewHolder holder, int position) {
holder.mBoundString = mValues.get(position);
holder.mTextView.setText(mValues.get(position));
//Here it is simply write onItemClick listener here
holder.mView.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
Context context = v.getContext();
Intent intent = new Intent(context, ExampleActivity.class);
context.startActivity(intent);
}
});
}
#Override
public int getItemCount() {
return mValues.size();
}
}
You can pass a clickListener to Adapter.
In your Activity:
private View.OnClickListener mItemClick = new View.OnClickListener() {
#Override
public void onClick(View v) {
Intent intent = null;
int position = list.getChildPosition(v);
switch (position) {
case 0:
intent = new Intent(MainActivity.this, LeakCanaryActivity.class);
break;
case 1:
intent = new Intent(MainActivity.this, ButterKnifeFragmentActivity.class);
break;
}
if (intent != null) {
MainActivity.this.startActivity(intent);
}
}
};
then pass it to Adapter:
MainAdapter mainAdapter = new MainAdapter(this, mItemClick);
In Adapter's onCreateViewHolder:
#Override
public MainAdapter.ViewHolder onCreateViewHolder(ViewGroup viewGroup, int position) {
View itemView = activity.getLayoutInflater().inflate(R.layout.main_adapter_item, viewGroup, false);
ViewHolder holder = new ViewHolder(itemView);
itemView.setOnClickListener(mItemClick);
return holder;
}
If you want to catch click event On Individual items then just implement OnClickListener in ViewHolder class and then set click listeners on individual views or whole itemView.
Following example shows the same
public class ContactViewHolder extends RecyclerView.ViewHolder implements OnClickListener
{
TextView txt_title,txt_name,txt_email;
public ContactViewHolder(View itemView)
{
super(itemView);
txt_title = (TextView)itemView.findViewById(R.id.txt_title);
txt_name = (TextView)itemView.findViewById(R.id.txt_name);
txt_email = (TextView)itemView.findViewById(R.id.txt_email);
txt_name.setOnClickListener(this);
txt_email.setOnClickListener(this);
itemView.setOnClickListener(this);
}
#Override
public void onClick(View v) {
// TODO Auto-generated method stub
if(v == itemView)
{
Toast.makeText(RecyclerDemoActivity.this, "Visiting Card Clicked is ==>"+txt_name.getText(), Toast.LENGTH_SHORT).show();
}
if(v == txt_name)
{
Toast.makeText(RecyclerDemoActivity.this, "Name ==>"+txt_name.getText(), Toast.LENGTH_SHORT).show();
}
if(v == txt_email)
{
Toast.makeText(RecyclerDemoActivity.this, "Email ==>"+txt_email.getText(), Toast.LENGTH_SHORT).show();
}
}
}
}
You can implement View.OnClickListener to your ViewHolder class
public static class ViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener {
public Item item
#InjectView(R.id.tv_title)
public TextView tvTitle;
#InjectView(R.id.rl_row)
public RelativeLayout rlRow;
public ViewHolder(View v) {
super(v);
ButterKnife.inject(this, v);
v.setOnClickListener(this);
}
#Override
public void onClick(View view) {
Log.e("item title",item.getTitle());
}
}
And onBindViewHolder set your view holder's item:
public void onBindViewHolder(ViewHolder holder, int position) {
holder.tvTitle.setText(objects.get(position).getTitle());
holder.item = objects.get(position);
}
I have developed a light weighted library for android, you can visit github and follow this sample:
RecycleClick.addTo(YOUR_RECYCLE_VIEW).setOnItemClickListener(new RecycleClick.OnItemClickListener() {
#Override
public void onItemClicked(RecyclerView recyclerView, int position, View v) {
// Your code here
}
});
According to Yigit Boyar, the best way to register a click on a RecyclerView is to define the click in the creation of the ViewHolder instead of just creating a new onClickListener for each item that the onBindViewHolder binds
Example:
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): BaseViewHolder<*> {
val itemBinding = LayoutInflater.from(context).inflate(R.layout.my_layout, parent, false)
val vh = MainViewHolder (itemBinding)
vh.itemView.setOnClickListener {
val pos = vh.adapterPosition
if(pos != NO_POSITION){
itemClickLister.onCocktailClick(myList[pos],pos)
}
}
return vh
}
All the answers posted so far are great solutions, however if you do not want to deal with too many implementation details, and just want it to work similarly to how ListView does, I would recommend using TwoWay-View, as seen here:
https://github.com/lucasr/twoway-view
Note that this implementation also supports long press on items, as well as support for pressed states (which is something important that other solutions to this question lack).
If you don't want to use the entire library, take a look at the ClickItemTouchListener class, which can be used as a standalone if needed. The only issue I found with it at the moment is with long press + scrolling, it seems to have incorrect behaviour.
You can easily define setOnClickListener in your ViewHolder class as follow:
public class ViewHolder extends RecyclerView.ViewHolder {
TextView product_name;
ViewHolder(View itemView) {
super(itemView);
product_name = (TextView) itemView.findViewById(R.id.product_name);
itemView.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
int itemPosition = getLayoutPosition();
Toast.makeText(getApplicationContext(), itemPosition + ":" + String.valueOf(product_name.getText()), Toast.LENGTH_SHORT).show();
}
});
}
}
For me, this is the best way:
class YourRecyclerAdapter extends RecyclerView.Adapter<ContactViewHolder> implements View.OnClickListener {
...
#Override
public void onClick(View view) {
int itemPosition = vRecycle.getChildPosition(view);
//And use itemPosition to get the item from your collection. This way you dont restrain the ViewHolder with a OnClick callback
}
...
}
The RecyclerView does not have a OnClickListener and will have to implement it ourselves.
I like to add a OnItemClickListener interface in Adapter with an onClick method invoked when you click on the item view from the ViewHolder. Thus the responsibility of managing the click on an item is outside the ViewHolder and Adapter. Will the activity or fragment which will decide what to do
Add an interface to the listener and the listener object.
public class ItemsAdapter extends RecyclerView.Adapter<ItemsAdapter.ViewHolder> {
...
private static OnItemClickListener onItemClickListener;
...
public static interface OnItemClickListener {
public void onItemClick(View view, int position);
}
...
}
We capture the click of the root view of the item and when the callback is triggered onClick listener call on the adapter .
public class ItemsAdapter extends RecyclerView.Adapter<ItemsAdapter.ViewHolder> {
...
private static OnItemClickListener onItemClickListener;
...
public static interface OnItemClickListener {
public void onItemClick(View view, int position);
}
...
public static class ViewHolder extends RecyclerView.ViewHolder {
public ImageView imageView;
public ViewHolder(View itemRootView) {
super(itemRootView);
imageView = (ImageView) itemRootView.findViewById(R.id.itemImage);
itemRootView.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
int position = ViewHolder.super.getAdapterPosition();
onItemClickListener.onItemClick(view,position);
}
});
}
}
}
Since the activity or fragment , fragment in our case , we assign a listener to the adapter and the onClick callback we will get the selected item by position and opened a detailed activity of item.
public class ItemsFragment extends Fragment {
...
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
...
((ItemsAdapter) adapter).setOnItemClickListener(new ItemsAdapter.OnItemClickListener() {
#Override
public void onItemClick(View view, int position) {
//Do something when an item has been clicked
}
});
...
}
...
}
Here is what I did Read more & download the gist here
Adding the same here
CustomItemClickListener.java
public interface CustomItemClickListener {
public void onItemClick(View v, int position);
}
ItemsListAdapter.java
public class ItemsListAdapter extends RecyclerView.Adapter<ItemsListAdapter.ViewHolder> {
ArrayList<ItemListSingleItem> data;
Context mContext;
CustomItemClickListener listener;
#Override
public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View mView = LayoutInflater.from(parent.getContext()).inflate(R.layout.items_list_single_item, parent, false);
final ViewHolder mViewHolder = new ViewHolder(mView);
mView.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
listener.onItemClick(v, mViewHolder.getAdapterPosition());
}
});
return mViewHolder;
}
#Override
public void onBindViewHolder(ViewHolder holder, int position) {
holder.itemTitle.setText(Html.fromHtml(data.get(position).getTitle()));
if (!TextUtils.isEmpty(data.get(position).getThumbnailURL())) {
// I Love picasso library :) http://square.github.io/picasso/
Picasso.with(mContext).load(data.get(position).getThumbnailURL()).error(R.drawable.ic_no_image).
placeholder(R.drawable.ic_no_image).
transform(new RoundedCornersTransformation(5, 0)).
into(holder.thumbnailImage);
} else {
holder.thumbnailImage.setImageResource(R.drawable.ic_no_image);
}
}
#Override
public int getItemCount() {
return data.size();
}
public ItemsListAdapter(Context mContext, ArrayList<ItemsListSingleItem> data, CustomItemClickListener listener) {
this.data = data;
this.mContext = mContext;
this.listener = listener;
}
public static class ViewHolder extends RecyclerView.ViewHolder {
public TextView itemTitle;
public ImageView thumbnailImage;
ViewHolder(View v) {
super(v);
itemTitle = (TextView) v
.findViewById(R.id.post_title);
thumbnailImage = (ImageView) v.findViewById(R.id.post_thumb_image);
}
}
}
From most of the answers above, they seem to be setting their onclicklisteners to individual items. However, the solution am about to offer is very simple but yet not intuitive to many. Many are forgetting that the other components are always in a parent component which is used to display items in the List or Recycler views. This solution is just about setting a single onclick listener to this parent view and the turn is played. The solution also includes a way to pass the position of the item being clicked on from the list or recycler view. Here, our main rootview is a CardView from the android support library. Here is sample code
public class ListAdapter extends RecyclerView.Adapter<ListAdapter.ViewHolder> {
public static final String LOG_TAG = ListAdapter.class.getSimpleName();
private Cursor mDataset;
private Context mContext;
private ViewHolder mViewHolder;
// Provide a suitable constructor (depends on the kind of dataset)
public ListAdapter(Context context, Cursor Dataset) {
mDataset = Dataset;
mContext = context;
}
// Create new views (invoked by the layout manager)
#Override
public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
// create a new view
View v = LayoutInflater.from(parent.getContext())
.inflate(R.layout.list_business_view, parent, false);
mViewHolder = new ViewHolder(v);
return mViewHolder;
}
public void setData(Cursor newdata) {
this.mDataset = newdata;
}
// Replace the contents of a view (invoked by the layout manager)
#Override
public void onBindViewHolder(ViewHolder holder, int position) {
//Bind data to other items here. To save time, i have ommited that.
//here is where we attach a click listerner for an item in the recycler list rather than for each element of a given item.
holder.card.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
Toast.makeText(mContext, " Just cliked item at position " + itemPosition, Toast.LENGTH_LONG).show();
}
});
}
}
// Return the size of your dataset (invoked by the layout manager)
#Override
public int getItemCount() {
if (null != mDataset) {
return mDataset.getCount();
}
return 0;
}
// Provide a reference to the views for each data item
// Complex data items may need more than one view per item, and
// you provide access to all the views for a data item in a view holder
public static class ViewHolder extends RecyclerView.ViewHolder{
// each data item is just a string in this case
public final TextView mBusinesssName; // View for the business name
public final TextView mBusinessCategory; //View for the category name
public final ImageView businessImage; // View for the business category image Image
public final TextView mBusinessDistance; // View for the distance
public final CardView card;
public ViewHolder(View view) {
super(view);
mBusinesssName = (TextView) view.findViewById(R.id.list_item_name_textview);
mBusinessCategory = (TextView) view.findViewById(R.id.list_item_category_textview);
mBusinessDistance = (TextView) view.findViewById(R.id.list_item_dist_textview);
businessImage = (ImageView) view.findViewById(R.id.list_item_icon);
card = (CardView) view.findViewById(R.id.card_view);
}
}
}
Unfortunately RecyclerView is missing a couple of features that ListView had built-in.
For example the ability to add an OnItemClickListener that triggers when an item is clicked.
RecyclerView allows you to set an OnClickListener in your adapter, but passing on that click
listener from your calling code, to the adapter and to the ViewHolder, is complicated
for catching a simple item click.
public class ItemClickSupport {
private final RecyclerView mRecyclerView;
private OnItemClickListener mOnItemClickListener;
private OnItemLongClickListener mOnItemLongClickListener;
private View.OnClickListener mOnClickListener = new View.OnClickListener() {
#Override
public void onClick(View v) {
if (mOnItemClickListener != null) {
RecyclerView.ViewHolder holder = mRecyclerView.getChildViewHolder(v);
mOnItemClickListener.onItemClicked(mRecyclerView, holder.getAdapterPosition(), v);
}
}
};
private View.OnLongClickListener mOnLongClickListener = new View.OnLongClickListener() {
#Override
public boolean onLongClick(View v) {
if (mOnItemLongClickListener != null) {
RecyclerView.ViewHolder holder = mRecyclerView.getChildViewHolder(v);
return mOnItemLongClickListener.onItemLongClicked(mRecyclerView, holder.getAdapterPosition(), v);
}
return false;
}
};
private RecyclerView.OnChildAttachStateChangeListener mAttachListener
= new RecyclerView.OnChildAttachStateChangeListener() {
#Override
public void onChildViewAttachedToWindow(View view) {
if (mOnItemClickListener != null) {
view.setOnClickListener(mOnClickListener);
}
if (mOnItemLongClickListener != null) {
view.setOnLongClickListener(mOnLongClickListener);
}
}
#Override
public void onChildViewDetachedFromWindow(View view) {
}
};
private ItemClickSupport(RecyclerView recyclerView) {
mRecyclerView = recyclerView;
mRecyclerView.setTag(R.id.item_click_support, this);
mRecyclerView.addOnChildAttachStateChangeListener(mAttachListener);
}
public static ItemClickSupport addTo(RecyclerView view) {
ItemClickSupport support = (ItemClickSupport) view.getTag(R.id.item_click_support);
if (support == null) {
support = new ItemClickSupport(view);
}
return support;
}
public static ItemClickSupport removeFrom(RecyclerView view) {
ItemClickSupport support = (ItemClickSupport) view.getTag(R.id.item_click_support);
if (support != null) {
support.detach(view);
}
return support;
}
public ItemClickSupport setOnItemClickListener(OnItemClickListener listener) {
mOnItemClickListener = listener;
return this;
}
public ItemClickSupport setOnItemLongClickListener(OnItemLongClickListener listener) {
mOnItemLongClickListener = listener;
return this;
}
private void detach(RecyclerView view) {
view.removeOnChildAttachStateChangeListener(mAttachListener);
view.setTag(R.id.item_click_support, null);
}
public interface OnItemClickListener {
void onItemClicked(RecyclerView recyclerView, int position, View v);
}
public interface OnItemLongClickListener {
boolean onItemLongClicked(RecyclerView recyclerView, int position, View v);
}
}
You also need to define R.id.item_click_support using ids.xml:
<?xml version="1.0" encoding="utf-8"?>
<resources>
<item name="item_click_support" type="id" />
</resources>
The resulting code click listener now looks like this:
ItemClickSupport.addTo(mRecyclerView).setOnItemClickListener(new ItemClickSupport.OnItemClickListener() {
#Override
public void onItemClicked(RecyclerView recyclerView, int position, View v) {
// do it
}
});
For Brief Explanation about recyclerview clicks please have a look at this littlerobots_blog
Kotlin implementation of nhaarman's answer :
mRecyclerView.addOnItemTouchListener(object : RecyclerItemClickListener(this, mRecyclerView,object :RecyclerItemClickListener.OnItemClickListener{
override fun onItemClick(view: View, position: Int) {
}
override fun onLongItemClick(view: View?, position: Int) {
}
}){})
RecyclerItemClickListener.java :
import android.content.Context
import android.support.v7.widget.RecyclerView
import android.view.GestureDetector
import android.view.MotionEvent
import android.view.View
open class RecyclerItemClickListener(context: Context, recyclerView: RecyclerView, private val mListener: OnItemClickListener?) : RecyclerView.OnItemTouchListener {
private var mGestureDetector: GestureDetector
interface OnItemClickListener {
fun onItemClick(view: View, position: Int)
fun onLongItemClick(view: View?, position: Int)
}
init {
mGestureDetector = GestureDetector(context, object : GestureDetector.SimpleOnGestureListener() {
override fun onSingleTapUp(e: MotionEvent): Boolean {
return true
}
override fun onLongPress(e: MotionEvent) {
val child = recyclerView.findChildViewUnder(e.x, e.y)
if (child != null && mListener != null) {
mListener.onLongItemClick(child, recyclerView.getChildAdapterPosition(child))
}
}
})
}
override fun onInterceptTouchEvent(view: RecyclerView, e: MotionEvent): Boolean {
val childView = view.findChildViewUnder(e.x, e.y)
if (childView != null && mListener != null && mGestureDetector.onTouchEvent(e)) {
mListener.onItemClick(childView, view.getChildAdapterPosition(childView))
return true
}
return false
}
override fun onTouchEvent(view: RecyclerView, motionEvent: MotionEvent) {}
override fun onRequestDisallowInterceptTouchEvent(disallowIntercept: Boolean) {}
}
Here is simple and clear way is add inside your ReacyclerView ViewHolder
public static class MyViewholder extends RecyclerView.ViewHolder {
public MyViewholder(View itemView) {
super(itemView);
itemView.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View v) {
Log.d("Tag", "onClick:" + getAdapterPosition());
}
});
}
}
getAdapterPosition() is returns the current clicked item position
Don't reinvent the wheel! The code for this specific use case is included in the Master/Detail Flow starter project that comes with Android Studio.
From Android Studio select:
File > New > New Project....
In the Phone and Tablet tab select Master/Detail Flow as shown below.
Create the project as either Kotlin or Java.
Profit.
I am not going to include here the code from google's ootb the demo project, but I'll outline the main design approaches in the sample provided by google:
the item OnClickListener is created ONLY ONCE, and is assigned to a field in your RecyclerView.Adapter implementation.
in the onBindViewHolder() you should set the same, pre-created onClickListener object on your ViewHolder instance with holder.itemView.setOnClickListener(mOnClickListener) (AVOID creating a new instance on every method call!); if you need to capture clicks on some specific elements inside the ViewHolder then extend ViewHolder and expose the elements you need as fields so that you can attach whatever listeners you need in onBindViewHolder() — and once again, do NOT re-create the listeners on every method call — initialise them as instance fields and attach them as needed.
you can use .setTag() in order to pass state to your viewHolder, e.g. holder.itemView.setTag(mValues.get(position)); as used in the demo.
I'm aware there are a lot of answers, but I thought I might just provide my implementation of it as well. (Full details can be found on another question I answered).
So, to add a click listener, your inner ViewHolder class needs to implement View.OnClickListener. This is because you will set an OnClickListener to the itemView parameter of the ViewHolder's constructor. Let me show you what I mean:
public class ExampleClickViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener {
TextView text1, text2;
ExampleClickViewHolder(View itemView) {
super(itemView);
// we do this because we want to check when an item has been clicked:
itemView.setOnClickListener(this);
// now, like before, we assign our View variables
title = (TextView) itemView.findViewById(R.id.text1);
subtitle = (TextView) itemView.findViewById(R.id.text2);
}
#Override
public void onClick(View v) {
// The user may not set a click listener for list items, in which case our listener
// will be null, so we need to check for this
if (mOnEntryClickListener != null) {
mOnEntryClickListener.onEntryClick(v, getLayoutPosition());
}
}
}
The only other things you need to add are a custom interface for your Adapter and a setter method:
private OnEntryClickListener mOnEntryClickListener;
public interface OnEntryClickListener {
void onEntryClick(View view, int position);
}
public void setOnEntryClickListener(OnEntryClickListener onEntryClickListener) {
mOnEntryClickListener = onEntryClickListener;
}
So your new, click-supporting Adapter is complete.
Now, let's use it...
ExampleClickAdapter clickAdapter = new ExampleClickAdapter(yourObjects);
clickAdapter.setOnEntryClickListener(new ExampleClickAdapter.OnEntryClickListener() {
#Override
public void onEntryClick(View view, int position) {
// stuff that will happen when a list item is clicked
}
});
It's basically how you would set up a normal Adapter, except that you use your setter method that you created to control what you will do when your user clicks a particular list item.
You can also look through a set of examples I made on this Gist on GitHub:
https://gist.github.com/FarbodSalamat-Zadeh/7646564f48ee708c1582c013e1de4f07
here is complete code for my custom adapter this code will inflate the rows with list items defined in the xml file named "list_item" it will also perform click event on all list items rows with respective positions.
public class MyCustomAdapter extends RecyclerView.Adapter`<`AdapterMyCustomAdapter.ViewHolder> {
public static class ViewHolder extends RecyclerView.ViewHolder implements OnClickListener {
public onItemClickListener mListener;
public ViewHolder(View v, onItemClickListener listener) {
super(v);
mListener =listener;
v.setOnClickListener(this);
}
#Override
public void onClick(View v) {
mListener.onRecyclerItemClick(v, getPosition());
}
public static interface onItemClickListener {
public void onRecyclerItemClick(View view , int position);
}
}
#Override
public int getItemCount() {
return 5;
}
#Override
public void onBindViewHolder(ViewHolder holder, int pos) {
}
#Override
public ViewHolder onCreateViewHolder(ViewGroup parent, int position) {
View v = LayoutInflater.from(parent.getContext())
.inflate(R.layout.list_item, parent, false);
/* here list_item is an xml file we want to inflate ...it is same as we do in case of listview for customization.*/
MyCustomAdapter.ViewHolder vh = new ViewHolder(v, new MyCustomAdapter.ViewHolder.onItemClickListener() {
#Override
public void onRecyclerItemClick(View view, int position) {
System.out.println("clicked on list item at position " +position);
}
});
return vh;
}
}
Setup click listener inside your ViewHolder this way:
public class MyViewHolder extends RecyclerView.ViewHolder {
public TextView title, year, genre;
public MyViewHolder(View view) {
super(view);
title = (TextView) view.findViewById(R.id.title);
genre = (TextView) view.findViewById(R.id.genre);
year = (TextView) view.findViewById(R.id.year);
view.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
Toast.makeText(context, ""+getAdapterPosition(), Toast.LENGTH_SHORT).show();
}
});
}
}
so i tried to implement onClickListener for my RecyclerView for the first time and I've been wondering if what i did is actually worth doing. In my application i have different Recycler Views, and i don't need both onClickListeners and onLongClickListeners at the same time in most of them, so I wanted to do it so that i don't have to implement them both in my .setOnItemClickListener. I basically check which onClickListener is set up with enum mode, and then according to that i set my listeners in onCreateViewHolder. Does it even makes sense to do that? Or should i just implement both listeners and don't do what i did in my code?
public class FreindRecyclerViewAdapter extends RecyclerView.Adapter<FreindRecyclerViewAdapter.MyViewHolder> {
private ClickListener clickListener ;
private LongClickListener longClickListener;
private Context context;
private List<String> friends;
private ListenerMode mode;
public enum ListenerMode {NullMode, ShortClick, LongClick}
public interface ClickListener {
void onItemClick(int position, View v);
}
public interface LongClickListener {
void onItemLongClick(int position, View v);
}
public void setOnItemClickListener(ClickListener clickListener) {
this.clickListener = clickListener;
mode = ListenerMode.ShortClick;
}
public void setOnLongItemClickListener(LongClickListener longItemClickListener) {
this.longClickListener = longItemClickListener;
mode = ListenerMode.LongClick;
}
public FreindRecyclerViewAdapter (Context context, List<String> friends) {
this.context = context;
this.friends = friends;
this.mode = ListenerMode.NullMode;
}
#NonNull
#Override
public MyViewHolder onCreateViewHolder(#NonNull ViewGroup viewGroup, int i) {
View view;
view = LayoutInflater.from(context).inflate(R.layout.friend_item, viewGroup, false);
final MyViewHolder myViewHolder = new MyViewHolder(view);
if(mode == ListenerMode.ShortClick) {
myViewHolder.itemView.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
clickListener.onItemClick(myViewHolder.getAdapterPosition(), view);
}
});
} else if (mode == ListenerMode.LongClick) {
myViewHolder.itemView.setOnLongClickListener(new View.OnLongClickListener() {
#Override
public boolean onLongClick(View view) {
longClickListener.onItemLongClick(myViewHolder.getAdapterPosition(), view);
return true;
}
});
}
return myViewHolder;
}
#Override
public void onBindViewHolder(#NonNull MyViewHolder myViewHolder, int position) {
myViewHolder.friendName.setText(friends.get(position));
}
#Override
public int getItemCount() {
return friends.size();
}
public static class MyViewHolder extends RecyclerView.ViewHolder {
private TextView friendName;
public MyViewHolder(View itemView) {
super(itemView);
friendName= itemView.findViewById(R.id.friendName_ID);
}
}
}
EDIT: Now that i think of it, i don't even know why it does work, like when i set up my RecyclerView, adapter and all in my code below, I first tell the adapter about the list I want to show on the screen in it's constructor recyclerViewAdapter = new FreindRecyclerViewAdapter(this, friends);, so this is when onCreateViewHolder is called for my views. So now when everything is created (ViewHolders) I call the function to set up the OnClickListener, which in my code adds different listeners according to the mode that is set up, and those listeners are added in onCreateViewHolder, which was already called, so why does RecyclerViewAdapter (and how does it know to) call the onCreateViewHolder again to add the listeners?
friends = new ArrayList<>();
friends.add("Josh");
friends.add("Mike");
friends.add("Ashley");
friends.add("Jess");
LinearLayoutManager linearLayoutManager = new LinearLayoutManager(this);
recyclerView = findViewById(R.id.recyclerViewFriend_ID);
recyclerViewAdapter = new FreindRecyclerViewAdapter(this, friends);
recyclerView.setLayoutManager(linearLayoutManager);
recyclerView.setAdapter(recyclerViewAdapter);
DividerItemDecoration itemDecor = new DividerItemDecoration(this, linearLayoutManager.getOrientation());
recyclerView.addItemDecoration(itemDecor);
recyclerViewAdapter.setOnLongItemClickListener(new FreindRecyclerViewAdapter.LongClickListener() {
#Override
public void onItemLongClick(int position, View v) {
Toast.makeText(FriendActivity.this, "Long Click. Position:" + Integer.toString(position), Toast.LENGTH_SHORT).show();
}
});
It's ok but it can be better.
1) You are creating a click listener and long click listener for every position
You don't need to create a listener for every position like you are doing here:
myViewHolder.itemView.setOnClickListener(new View.OnClickListener() ...
and
myViewHolder.itemView.setOnLongClickListener(new View.OnLongClickListener() ...
Since they are performing the very similar action, you can create only one click and longclick listener and share with all views. To make that, move the itemView.setOnClickListener() and the itemView.setOnLongClickListener() to the ViewHolder class.
You need to save the position in the ViewHolder as well. So, they will be able to store their own position.
2) You don't need to create a enum
You don't need to create a enum to check if current mode (click or long click). Instead, you can just check if the variables are null, for example.
In the end, you can have a code like this:
public class FreindRecyclerViewAdapter extends RecyclerView.Adapter<FreindRecyclerViewAdapter.MyViewHolder> {
private ClickListener clickListener ;
private LongClickListener longClickListener;
private Context context;
private List<String> friends;
public interface ClickListener {
void onItemClick(int position, View v);
}
public interface LongClickListener {
void onItemLongClick(int position, View v);
}
public void setOnItemClickListener(ClickListener clickListener) {
this.clickListener = clickListener;
}
public void setOnLongItemClickListener(LongClickListener longItemClickListener) {
this.longClickListener = longItemClickListener;
}
#NonNull
#Override
public MyViewHolder onCreateViewHolder(#NonNull ViewGroup viewGroup, int i) {
View view;
view = LayoutInflater.from(context).inflate(R.layout.friend_item, viewGroup, false);
return new MyViewHolder(view, i, clickListener, longClickListener);;
}
#Override
public void onBindViewHolder(#NonNull MyViewHolder myViewHolder, int position) {
myViewHolder.friendName.setText(friends.get(position));
}
#Override
public int getItemCount() {
return friends.size();
}
public class MyViewHolder extends RecyclerView.ViewHolder implements
View.OnLongClickListener, View.OnClickListener {
private TextView friendName;
private int position;
public MyViewHolder(View itemView, int position) {
super(itemView);
friendName = itemView.findViewById(R.id.friendName_ID);
this.position = position;
if (clickListener != null) {
itemView.setOnClickListener(this);
}
if (longClickListener != null) {
itemView.setOnLongClickListener(this);
}
}
#Override
public void onClick(View view) {
if (clickListener != null) {
clickListener.onItemClick(position, view);
}
}
#Override
public boolean onLongClick(View view) {
if (longClickListener != null) {
longClickListener.onItemLongClick(position, view);
return true;
} else {
return false;
}
}
}
}
Note that now, each view holder knows its own position
View holder implements the regular View.OnClickListener and View.LongClickListener. So, you don't need to instantiate a new listener for every position.
If you want to enable the click, call FreindRecyclerViewAdapter.setOnItemClickListener(object);
If you want to enable the long click, call FreindRecyclerViewAdapter.setOnLongItemClickListener(object);
If you want to disable any of them, don't call the methods above or just call them passing null as parameter. If you check the code in MyViewHolder, you can see that any action is performed when those listeners are null
Hope I could help and share more ways to achieve what you want!!!
I want to set up an onClickListener within my RecyclerView adapter so I can easily refer to the other views to retrieve their tags.
Is it possible to set up an onClickListener in a RecyclerView adapter? How would I do it? Would it affect performance?
Here is my RecyclerView adapter:
public class PostRecyclerAdapter extends RecyclerView.Adapter<PostRecyclerAdapter.ViewHolder> {
private Context context;
private List<Post> mDataset;
public static class ViewHolder extends RecyclerView.ViewHolder implements View.OnCreateContextMenuListener {
public LinearLayout mainLayout;
public TextView username;
public ImageView image;
public ViewHolder(LinearLayout view) {
super(view);
view.setOnCreateContextMenuListener(this);
mainLayout = (LinearLayout) view.findViewById(R.id.main_view);
username = (TextView) view.findViewById(R.id.username);
image = (ImageView) view.findViewById(R.id.image);
}
}
public PostRecyclerAdapter(Context context, List<Post> myDataset) {
this.context = context;
this.mDataset = myDataset;
}
#Override
public PostRecyclerAdapter.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.post_layout, parent, false);
ViewHolder vh = new ViewHolder((LinearLayout) view);
return vh;
}
#Override
public void onBindViewHolder(ViewHolder holder, int position) {
Post postItem = mDataset.get(position);
holder.username.setText(postItem.getUserame());
}
#Override
public int getItemCount() {
return mDataset.size();
}
}
I would like to suggest a much simpler approach than the one given above.
in your adapter you would have written static view holder class right for that add the following code
public static class ViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener{
// your views declared here
CardView cardView;
public ViewHolder(View itemView) {
super(itemView);
// your view initialised here
cardView=itemView.findViewById(R.id.card_view);
cardView.setOnClickListener(this);
}
#Override
public void onClick(View v) {
switch (v.getId()){
case R.id.card_view:
// your logic
break;
}
}
}
by doing so you can handle individual click of your layout as well as the entire layout click
You can use GestureDetector for this. This is very simple to use :
Create A class RecyclerTouchListener :
public class RecyclerTouchListener implements RecyclerView.OnItemTouchListener {
private GestureDetector gestureDetector;
private ClickListener clickListener;
public RecyclerTouchListener(Context context, final RecyclerView recyclerView, final ClickListener clickListener) {
this.clickListener = clickListener;
gestureDetector = new GestureDetector(context, new GestureDetector.SimpleOnGestureListener() {
#Override
public boolean onSingleTapUp(MotionEvent e) {
return true;
}
#Override
public void onLongPress(MotionEvent e) {
View child = recyclerView.findChildViewUnder(e.getX(), e.getY());
if (child != null && clickListener != null) {
clickListener.onLongClick(child, recyclerView.getChildAdapterPosition(child));
}
}
});
}
#Override
public boolean onInterceptTouchEvent(RecyclerView rv, MotionEvent e) {
View child = rv.findChildViewUnder(e.getX(), e.getY());
if (child != null && clickListener != null && gestureDetector.onTouchEvent(e)) {
clickListener.onClick(child, rv.getChildPosition(child));
}
return false;
}
#Override
public void onTouchEvent(RecyclerView rv, MotionEvent e) {
}
#Override
public void onRequestDisallowInterceptTouchEvent(boolean disallowIntercept) {
}
public interface ClickListener {
void onClick(View view, int position);
void onLongClick(View view, int position);
}
}
and you can use this class as follow :
rvFilterOptions = (RecyclerView) rootView.findViewById(R.id.rvCommon);
rvFilterOptions.setHasFixedSize(true);
rvFilterOptions.addItemDecoration(new DividerItemDecoration(getActivity(),
DividerItemDecoration.VERTICAL_LIST));
LinearLayoutManager mLayoutManager = new LinearLayoutManager(getActivity());
rvFilterOptions.setLayoutManager(mLayoutManager);
rvFilterOptions.addOnItemTouchListener(new RecyclerTouchListener(getActivity(), rvFilterOptions, new RecyclerTouchListener.ClickListener() {
#Override
public void onClick(View view, int position) {
// Perform click operation and you can get post item from array using position
}
#Override
public void onLongClick(View view, int position) {
// Perform Long click operation
}
}));
Note : Concept is taken from LINK
This is simplest implementation but this will be inside recyclerviews adapter. If you want to get row item clicklistener in activity then use Interface.
public class PostRecyclerAdapter extends RecyclerView.Adapter<PostRecyclerAdapter.ViewHolder> {
private Context context;
private List<Post> mDataset;
public static class ViewHolder extends RecyclerView.ViewHolder implements View.OnCreateContextMenuListener {
public LinearLayout mainLayout;
public TextView username;
public ImageView image;
public View mRowItem;
public ViewHolder(LinearLayout view) {
super(view);
view.setOnCreateContextMenuListener(this);
mRowItem = view;
mainLayout = (LinearLayout) view.findViewById(R.id.main_view);
username = (TextView) view.findViewById(R.id.username);
image = (ImageView) view.findViewById(R.id.image);
}
}
public PostRecyclerAdapter(Context context, List<Post> myDataset) {
this.context = context;
this.mDataset = myDataset;
}
#Override
public PostRecyclerAdapter.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.post_layout, parent, false);
ViewHolder vh = new ViewHolder((LinearLayout) view);
return vh;
}
#Override
public void onBindViewHolder(ViewHolder holder, int position) {
Post postItem = mDataset.get(position);
holder.username.setText(postItem.getUserame());
//Whole row item
holder.mRowItem.setTag(position);
holder.mRowItem.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
int pos = (int) v.getTag();
// handle your row item click.
}
});
}
#Override
public int getItemCount() {
return mDataset.size();
}
}
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!
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().
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();
}
}
});
}
}
}
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;
}
}
}
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) {
}
}
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
}
}));
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
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) {
}
}
)
);
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();
...
}
}
}
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.
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
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
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;
...
}
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
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)
//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) {
}
});
}
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.
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)
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/
//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();
}
}
});
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.
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
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.
I am experimenting with the support library's recyclerview and cards. I have a recyclerview of cards. Each card has an 'x' icon at the top right corner to remove it:
The card xml, list_item.xml:
<?xml version="1.0" encoding="utf-8"?>
<android.support.v7.widget.CardView
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_margin="5dp">
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<TextView
android:id="#+id/taskDesc"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
android:textSize="40sp"
android:text="hi"/>
<ImageView
android:id="#+id/xImg"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentTop="true"
android:layout_alignParentRight="true"
android:src="#drawable/ic_remove"/>
</RelativeLayout>
</android.support.v7.widget.CardView>
I attempted to tag the row with the position I would use in notifyItemRemoved(position) in TaskAdapter.java:
public class TaskAdapter extends RecyclerView.Adapter<TaskAdapter.TaskViewHolder> {
private List<Task> taskList;
private TaskAdapter thisAdapter = this;
// cache of views to reduce number of findViewById calls
public static class TaskViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener {
protected TextView taskTV;
protected ImageView closeBtn;
public TaskViewHolder(View v) {
super(v);
taskTV = (TextView)v.findViewById(R.id.taskDesc);
}
#Override
public void onClick(View v) {
int position = v.getTag();
adapter.notifyItemRemoved(position);
}
}
public TaskAdapter(List<Task> tasks) {
if(tasks == null)
throw new IllegalArgumentException("tasks cannot be null");
taskList = tasks;
}
// onBindViewHolder binds a model to a viewholder
#Override
public void onBindViewHolder(TaskViewHolder taskViewHolder, int pos) {
final int position = pos;
Task currTask = taskList.get(pos);
taskViewHolder.taskTV.setText(currTask.getDescription());
taskViewHolder.closeBtn.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
thisAdapter.notifyItemRemoved(position);
}
});
}
#Override
public int getItemCount() {
return taskList.size();
}
// inflates row to create a viewHolder
#Override
public TaskViewHolder onCreateViewHolder(ViewGroup parent, int pos) {
View itemView = LayoutInflater.from(parent.getContext()).
inflate(R.layout.list_item, parent, false);
return new TaskViewHolder(itemView);
}
}
This won't work because you can't set a tag nor can I access the adapter from onClick.
Set your onClickListeners on onBindViewHolder() and you can access the position from there. If you set them in your ViewHolder you won't know what position was clicked unless you also pass the position into the ViewHolder
EDIT
As pskink pointed out ViewHolder has a getPosition() so the way you were originally doing it was correct.
When the view is clicked you can use getPosition() in your ViewHolder and it returns the position
Update
getPosition() is now deprecated and replaced with getAdapterPosition()
Update 2020
getAdapterPosition() is now deprecated and replaced with getAbsoluteAdapterPosition() or getBindingAdapterPosition()
Kotlin code:
override fun onBindViewHolder(holder: MyHolder, position: Int) {
// - get element from your dataset at this position
val item = myDataset.get(holder.absoluteAdapterPosition)
}
A different method - using setTag() and getTag() methods of the View class.
use setTag() in the onBindViewHolder method of your adapter
#Override
public void onBindViewHolder(myViewHolder viewHolder, int position) {
viewHolder.mCardView.setTag(position);
}
where mCardView is defined in the myViewHolder class
private class myViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener {
public View mCardView;
public myViewHolder(View view) {
super(view);
mCardView = (CardView) view.findViewById(R.id.card_view);
mCardView.setOnClickListener(this);
}
}
use getTag() in your OnClickListener implementation
#Override
public void onClick(View view) {
int position = (int) view.getTag();
//display toast with position of cardview in recyclerview list upon click
Toast.makeText(view.getContext(),Integer.toString(position),Toast.LENGTH_SHORT).show();
}
see https://stackoverflow.com/a/33027953/4658957 for more details
To complement #tyczj answer:
Generic Adapter Pseido code:
public abstract class GenericRecycleAdapter<T, K extends RecyclerView.ViewHolder> extends RecyclerView.Adapter{
private List<T> mList;
//default implementation code
public abstract int getLayout();
#Override
public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View v = LayoutInflater.from(parent.getContext())
.inflate(getLayout(), parent, false);
return getCustomHolder(v);
}
public Holders.TextImageHolder getCustomHolder(View v) {
return new Holders.TextImageHolder(v){
#Override
public void onClick(View v) {
onItem(mList.get(this.getAdapterPosition()));
}
};
}
abstract void onItem(T t);
#Override
public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
onSet(mList.get(position), (K) holder);
}
public abstract void onSet(T item, K holder);
}
ViewHolder:
public class Holders {
public static class TextImageHolder extends RecyclerView.ViewHolder implements View.OnClickListener{
public TextView text;
public TextImageHolder(View itemView) {
super(itemView);
text = (TextView) itemView.findViewById(R.id.text);
text.setOnClickListener(this);
}
#Override
public void onClick(View v) {
}
}
}
Adapter usage:
public class CategoriesAdapter extends GenericRecycleAdapter<Category, Holders.TextImageHolder> {
public CategoriesAdapter(List<Category> list, Context context) {
super(list, context);
}
#Override
void onItem(Category category) {
}
#Override
public int getLayout() {
return R.layout.categories_row;
}
#Override
public void onSet(Category item, Holders.TextImageHolder holder) {
}
}
public static class ViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener {
FrameLayout root;
public ViewHolder(View itemView) {
super(itemView);
root = (FrameLayout) itemView.findViewById(R.id.root);
root.setOnClickListener(this);
}
#Override
public void onClick(View v) {
LogUtils.errorLog("POS_CLICKED: ",""+getAdapterPosition());
}
}
Get focused child, and use it to get position in adapter.
mRecyclerView.getChildAdapterPosition(mRecyclerView.getFocusedChild())
Personally, the simplest way that I have found and works great for me is as follows:
Create an interface inside your "RecycleAdapter" Class (Subclass)
public interface ClickCallback {
void onItemClick(int position);
}
Add a variable of the interface as a parameter in the Constructor.
private String[] items;
private ClickCallback callback;
public RecyclerAdapter(String[] items, ClickCallback clickCallback) {
this.items = items;
this.callback = clickCallback;
}
Set a Click listener in the ViewHolder (another subclass) and pass the 'position' to through the interface
AwesomeViewHolder(View itemView) {
super(itemView);
itemView.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
callback.onItemClick(getAdapterPosition());
}
});
mTextView = (TextView) itemView.findViewById(R.id.mTextView);
}
Now, when initializing the recycler adapter in an activity/fragment, just Create a new 'ClickCallback' (interface)
String[] values = {"Hello","World"};
RecyclerAdapter recyclerAdapter = new RecyclerAdapter(values, new RecyclerAdapter.ClickCallback() {
#Override
public void onItemClick(int position) {
// Do anything with the item position
}
});
That's it for me. :)
I solved this way
class MyOnClickListener implements View.OnClickListener {
#Override
public void onClick(View v) {
int itemPosition = mRecyclerView.getChildAdapterPosition(v);
myResult = results.get(itemPosition);
}
}
And in the adapter
#Override
public MyAdapter.ViewHolder onCreateViewHolder(ViewGroup parent,
int viewType) {
View v = LayoutInflater.from(parent.getContext()).inflate(R.layout.list_wifi, parent, false);
v.setOnClickListener(new MyOnClickListener());
ViewHolder vh = new ViewHolder(v);
return vh;
}
1. Create class Name RecyclerTouchListener.java
import android.content.Context;
import android.support.v7.widget.RecyclerView;
import android.view.GestureDetector;
import android.view.MotionEvent;
import android.view.View;
public class RecyclerTouchListener implements RecyclerView.OnItemTouchListener
{
private GestureDetector gestureDetector;
private ClickListener clickListener;
public RecyclerTouchListener(Context context, final RecyclerView recyclerView, final ClickListener clickListener) {
this.clickListener = clickListener;
gestureDetector = new GestureDetector(context, new GestureDetector.SimpleOnGestureListener() {
#Override
public boolean onSingleTapUp(MotionEvent e) {
return true;
}
#Override
public void onLongPress(MotionEvent e) {
View child = recyclerView.findChildViewUnder(e.getX(), e.getY());
if (child != null && clickListener != null) {
clickListener.onLongClick(child, recyclerView.getChildAdapterPosition(child));
}
}
});
}
#Override
public boolean onInterceptTouchEvent(RecyclerView rv, MotionEvent e) {
View child = rv.findChildViewUnder(e.getX(), e.getY());
if (child != null && clickListener != null && gestureDetector.onTouchEvent(e)) {
clickListener.onClick(child, rv.getChildAdapterPosition(child));
}
return false;
}
#Override
public void onTouchEvent(RecyclerView rv, MotionEvent e) {
}
#Override
public void onRequestDisallowInterceptTouchEvent(boolean disallowIntercept) {
}
public interface ClickListener {
void onClick(View view, int position);
void onLongClick(View view, int position);
}
}
2. Call RecyclerTouchListener
recycleView.addOnItemTouchListener(new RecyclerTouchListener(this, recycleView,
new RecyclerTouchListener.ClickListener() {
#Override
public void onClick(View view, int position) {
Toast.makeText(MainActivity.this,Integer.toString(position),Toast.LENGTH_SHORT).show();
}
#Override
public void onLongClick(View view, int position) {
}
}));
onBindViewHolder() is called for each and every item and setting the click listener inside onBindVieHolder() is an unnecessary option to repeat when you can call it once in your ViewHolder constructor.
public class MyViewHolder extends RecyclerView.ViewHolder
implements View.OnClickListener{
public final TextView textView;
public MyViewHolder(View view){
textView = (TextView) view.findViewById(R.id.text_view);
view.setOnClickListener(this);
// getAdapterPosition() retrieves the position here.
}
#Override
public void onClick(View v){
// Clicked on item
Toast.makeText(mContext, "Clicked on position: " + getAdapterPosition(), Toast.LENGTH_SHORT).show();
}
}
I think the most correct way to get item position is
View.OnClickListener onClickListener = new View.OnClickListener() {
#Override public void onClick(View v) {
View view = v;
View parent = (View) v.getParent();
while (!(parent instanceof RecyclerView)){
view=parent;
parent = (View) parent.getParent();
}
int position = recyclerView.getChildAdapterPosition(view);
}
Because view, you click not always the root view of your row layout. If view is not a root one (e.g buttons), you will get Class cast exception. Thus at first we need to find the view, which is the a dirrect child of you reciclerview. Then, find position using recyclerView.getChildAdapterPosition(view);
No need to have your ViewHolder implementing View.OnClickListener. You can get directly the clicked position by setting a click listener in the method onCreateViewHolder of RecyclerView.Adapter here is a sample of code :
public class ItemListAdapterRecycler extends RecyclerView.Adapter<ItemViewHolder>
{
private final List<Item> items;
public ItemListAdapterRecycler(List<Item> items)
{
this.items = items;
}
#Override
public ItemViewHolder onCreateViewHolder(final ViewGroup parent, int viewType)
{
View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.item_row, parent, false);
view.setOnClickListener(new View.OnClickListener()
{
#Override
public void onClick(View view)
{
int currentPosition = getClickedPosition(view);
Log.d("DEBUG", "" + currentPosition);
}
});
return new ItemViewHolder(view);
}
#Override
public void onBindViewHolder(ItemViewHolder itemViewHolder, int position)
{
...
}
#Override
public int getItemCount()
{
return items.size();
}
private int getClickedPosition(View clickedView)
{
RecyclerView recyclerView = (RecyclerView) clickedView.getParent();
ItemViewHolder currentViewHolder = (ItemViewHolder) recyclerView.getChildViewHolder(clickedView);
return currentViewHolder.getAdapterPosition();
}
}
#Override
public void onClick(View v) {
int pos = getAdapterPosition();
}
Simple as that, on ViewHolder
When using data binding and you need to know a RecyclerView click position from inside of an item's click listener:
Kotlin
val recyclerView = view.parent as RecyclerView
val position = recyclerView.getChildAdapterPosition(view)