Hide/show buttons inside recyclerview - android

I want to show and hide some buttons within the recycler view in recycler item clicked. For example I have a recyclerw view with two items like this
On click of the 1st item, the buttons Edit and delete should be displayed. My adapter code is here:
using Android.Support.V7.Widget;
using Android.Views;
using Android.Widget;
using System;
using System.Collections.Generic;
namespace RecyclerViewTest
{
internal class BuyerAdapter : RecyclerView.Adapter
{
public List<Buyer> mlivestock;
public event EventHandler<int> ItemClick;
public BuyerAdapter(List<Buyer> photoAlbum)
{
mlivestock = photoAlbum;
}
public override RecyclerView.ViewHolder
OnCreateViewHolder(ViewGroup parent, int viewType)
{
View itemView = LayoutInflater.From(parent.Context).
Inflate(Resource.Layout.PersonCardView, parent, false);
PhotoViewHolder vh = new PhotoViewHolder(itemView, ItemOnClick);
return vh;
}
void ItemOnClick(int position)
{
if (ItemClick != null)
ItemClick(this, position);
}
public override void
OnBindViewHolder(RecyclerView.ViewHolder holder, int position)
{
PhotoViewHolder vh = holder as PhotoViewHolder;
vh.Agent.Text = mlivestock[position].Agent;
vh.Pen.Text = mlivestock[position].Pen.ToString();
}
public override int ItemCount
{
get { return mlivestock.Count; }
}
}
public class PhotoViewHolder : RecyclerView.ViewHolder
{
public TextView Agent { get; private set; }
public TextView Pen { get; private set; }
public PhotoViewHolder(View itemView, Action<int> listener)
: base(itemView)
{
Agent = itemView.FindViewById<TextView>(Resource.Id.agent);
Pen = itemView.FindViewById<TextView>(Resource.Id.pen);
itemView.Click += (sender, e) => listener(base.AdapterPosition);
}
}
}
I have added a "ItemClick" event which should show/hide the buttons. I would also want to have click events for edit and delete buttons. How can I achieve this? Any help would be appreciated.

Set an adapter variable:
private int currentSelectedPosition = RecyclerView.NO_POSITION
Change your personCardView adapter layout to have both the buttons in them, and set their visibility to GONE. Reference them in your ViewHolder (e.g. Button editButton, Button deleteButton)
In your item onClickListener, set the currentPosition and call notifyDataSetChanged() - this is necessary to re-hide the buttons in previous selections and show the buttons in this selection. Then in onBindViewHolder, apply the VISIBLE or GONE logic as below. I personally set the itemClickListener inside onBindViewHolder too, so the whole method would look like this:
public void OnBindViewHolder(RecyclerView.ViewHolder holder, int position) {
holder.itemView.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
currentSelectedPosition = position;
notifyDataSetChanged();
}
});
if (currentSelectedPosition == position) {
holder.editButton.setVisibility(View.VISIBLE);
holder.editButton.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
// your edit button click event here
}
});
holder.deleteButton.setVisibility(View.VISIBLE);
holder.deleteButton.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
// your delete button click event here
}
});
} else {
holder.editButton.setVisibility(View.GONE);
holder.deleteButton.setVisibility(View.GONE);
}
//..... the rest of your code for onBindViewHolder (updating your text views and so on)
}

Related

How to implement onClickListener for CardView inside RecyclerView using Firebase data? [duplicate]

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();
}
});
}
}

Where should I use OnClickListner in firebase adapter? [duplicate]

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();
}
});
}
}

Disabling a RecyclerView

I'm facing the next problem.
I have a RecyclerView with the Adapter, the items, etc.
What I need is: Select an item in the recycler, change the color of that item, and then I have to disable the rest of the other items. It's like a RadioButton list.
I select one and disable the others. If I select again the same item, enable all the list.
I already have the onClick Button listener. I need to know if I have to reload again de list, If I have to loop and disable item by item, etc.
Thanks
public class MyAdapter extends RecyclerView.Adapter<MyAdapter.MyViewHolder> {
private List<Human> humans;
private HumanClick selectedHuman;
#Inject
public MyAdapter() {
}
public void init(List<Human> humanList, SelectedHumanClick humanClick){
this.humans = humanList;
this.humanClick = humanClick;
}
#NonNull
#Override
public MyViewHolder onCreateViewHolder(#NonNull ViewGroup parent, int viewType) {
View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.item_Human, parent, false);
return new MyViewHolder(view);
}
#Override
public void onBindViewHolder(#NonNull final MyViewHolder holder, int position) {
final Human human = Humans.get(position);
holder.bind(Humans.get(position));
holder.itemView.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
selectedHumanClick.onSelectedHumanClick(Human);
}
});
}
#Override
public int getItemCount() {
return humans != null ? Humans.size() : 0;
}
public final class MyViewHolder extends RecyclerView.ViewHolder {
#BindView(R.id.text_view_human_name)
TextView textView;
#BindView(R.id.image_view_profile)
ImageView imageViewProf;
#BindView(R.id.image_view_radio_btn)
ImageView imageViewRB;
private boolean isChecked = false;
public MyViewHolder(View itemView) {
super(itemView);
ButterKnife.bind(this, itemView);
}
public void bind(final Human Human) {
itemView.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
humanClick.onSelectedHumanClick(Human);
}
});
textView.setText(human.getName());
}
public void changeRBImage() {
if(!isChecked){
isChecked = true;
imageViewRB.setBackground(ContextCompat.getDrawable(imageViewRB.getContext(), R.drawable.selected));
}
else{
isChecked = false;
imageViewRB.setBackground(ContextCompat.getDrawable(imageViewRB.getContext(), R.drawable.not_selected));
}
}
}
My Recycler is defined inside a fragment here:
public class HumansFragment implenets HumanClick{
#BindView(R.id.recycler_humans)
RecyclerView humansRecyclerView;
#Inject
MyAdapter myAdapter;
.
.
.
public void loadHumans(List<Human> humans) {
myAdapter.init(Humans, this);
humansRecyclerView.setLayoutManager(new LinearLayoutManager(getContext()));
humansRecyclerView.setAdapter(myAdapter);
}
#Override
public void humansClick(Human human) {
//TODO
}
}
I'm thinking in something like this, using setOnItemClickListener and check the position of the item clicked. The position is the index of the item clicked check the last event and new, if both are equal change color, if not dont do anything like disabling.
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 recycler view
if(getLayoutPosition()==0){
//Do whatever you want here
}else if(getLayoutPosition()==1){
//Do whatever you want here
}else if(getLayoutPosition()==2){
}else if(getLayoutPosition()==3){
}else if(getLayoutPosition()==4){
}else if(getLayoutPosition()==5){
}
//or you can use For loop if you have long list of items. Use its length or size of the list as
for(int i = 0; i<exampleList.size(); i++){
}
}
}
Or something smaller
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
}
})
);

RecyclerView : get position and launch action [duplicate]

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();
}
});
}
}

how to bind a RecyclerView item and its containing button clicked event together

I have a recyclerView and its items contain a button. And I wanna get the recyclerview item position when I click on the button. Here is the problem, when the button clicked the button clicked event is triggered but the recyclerview item under neath is not, the clicked event can not go through the button on the top to recyclerview item.
How can I get this click-through event or how can I bind them together? Please someone tell me if know the answer.
update:
I could not find any similar post online. usually recyclerview contains button is register to different OnclickedListener. In my case, they need to handle the same listener, particularly the recyclerview handle it. But the button just block the clicked event of recyclerview. here is the viewholder code:
`public class GridViewAdapter extends RecyclerView.Adapter {
private final LinkedList<Device> mValues;
private final OnListFragmentInteractionListener mListener;
private OnLongClickListener mOnLongClickListener = null;
public GridViewAdapter(LinkedList<Device> items, OnListFragmentInteractionListener listener) {
mValues = items;
mListener = listener;
}
#Override
public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View view = LayoutInflater.from(parent.getContext())
.inflate(R.layout.device, parent, false);
return new ViewHolder(view);
}
#Override
public void onBindViewHolder(final ViewHolder holder, int position) {
holder.mItem = mValues.get(position);
// holder.mIconView.setImageResource(R.drawable.ic_switch);
holder.mNameView.setText(holder.mItem.device_name());
holder.mView.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
if (null != mListener) {
mListener.onDeviceClicked(holder.mItem);
}
}
});
}
public void deleteByDeviceCode(int deviceCode) {
for(int i=0;i<mValues.size();i++) {
if (mValues.get(i).device_code() == deviceCode) {
mValues.remove(i);
}
}
}
public void deleteByPosition(int pos) {
mValues.remove(pos);
}
public int getDeviceCodeByPosition(int pos) {
return mValues.get(pos).device_code();
}
#Override
public int getItemCount() {
return mValues.size();
}
public class ViewHolder extends RecyclerView.ViewHolder {
public final View mView;
public final Button mButton;
public final TextView mNameView;
public Device mItem;
public ViewHolder(View view) {
super(view);
mView = view;
mButton = (Button) view.findViewById(R.id.dev_button);
mNameView = (TextView) view.findViewById(R.id.dev_lable);
}
#Override
public String toString() {
return super.toString() + " '" + mNameView.getText() + "'";
}
}
}`
When creating view holders, try passing them index of element represented by them, or even reference to whole element. Now, when you've receiving click event in view holder, with event itself you should pass the (index of) element that is represented by that holder/row.

Categories

Resources