Set a click listener to a RecyclerView

妖精的绣舞 提交于 2019-11-28 11:11:58

You need to check this tutorial here for better understanding on how you can achieve the behaviour that you want.

In case of handling the onClickListener from your activity you need to work based on a callback implementation with an interface. Pass the interface from the activity to your adapter and then call the callback function from your adapter when some items are clicked.

Here's a sample implementation from the tutorial.

Let us first have the interface.

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

You need to modify your adapter to take the listener as the parameter like the one stated below.

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

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

Now in your onBindViewHolder method, set the click listener.

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

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

Now setting the adapter in your RecyclerView.

recycler.setAdapter(new ContentAdapter(items, new ContentAdapter.OnItemClickListener() {
    @Override public void onItemClick(ContentItem item) {
        Toast.makeText(getContext(), "Item Clicked", Toast.LENGTH_LONG).show();
    }
}));

So the whole adapter code looks like the following.

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

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

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

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

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

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

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

    static class ViewHolder extends RecyclerView.ViewHolder {

        private TextView name;
        private ImageView image;

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

        public void bind(final ContentItem item, final OnItemClickListener listener) {
            name.setText(item.name);
            Picasso.with(itemView.getContext()).load(item.imageUrl).into(image);
            itemView.setOnClickListener(new View.OnClickListener() {
                @Override public void onClick(View v) {
                    listener.onItemClick(item);
                }
            });
        }
    }
}

very simple and clean solution is:

  create a class with the name of RecyclerTouchListener:

    public class RecyclerTouchListener implements RecyclerView.OnItemTouchListener {

        private GestureDetector gestureDetector;
        private ClickListener clickListener;

        public RecyclerTouchListener(Context context, final RecyclerView recyclerView, final ClickListener clickListener) {
            this.clickListener = clickListener;
            gestureDetector = new GestureDetector(context, new GestureDetector.SimpleOnGestureListener() {
                @Override
                public boolean onSingleTapUp(MotionEvent e) {
                    return true;
                }

                @Override
                public void onLongPress(MotionEvent e) {
                    View child = recyclerView.findChildViewUnder(e.getX(), e.getY());
                    if (child != null && clickListener != null) {
                        clickListener.onLongClick(child, recyclerView.getChildPosition(child));
                    }
                }
            });
        }

        @Override
        public boolean onInterceptTouchEvent(RecyclerView rv, MotionEvent e) {

            View child = rv.findChildViewUnder(e.getX(), e.getY());
            if (child != null && clickListener != null && gestureDetector.onTouchEvent(e)) {
                clickListener.onClick(child, rv.getChildPosition(child));
            }
            return false;
        }

        @Override
        public void onTouchEvent(RecyclerView rv, MotionEvent e) {
        }

        @Override
        public void onRequestDisallowInterceptTouchEvent(boolean disallowIntercept) {

        }

        public interface ClickListener {
            void onClick(View view, int position);

            void onLongClick(View view, int position);
        }
    }

in your recyclerview activity:

recyclerView.addOnItemTouchListener(new RecyclerTouchListener(getApplicationContext(), recyclerView, new RecyclerTouchListener.ClickListener() {
            @Override
            public void onClick(View view, int position) {
                speech(countries_list_code[position]);
            }

            @Override
            public void onLongClick(View view, int position) {

            }
        }));

I found super duper easy method! I recommend this one

Example Code:

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

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

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

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

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

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

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

static class ViewHolder extends RecyclerView.ViewHolder {

    private TextView name;
    private ImageView image;

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

    public void bind(final ContentItem item, final OnItemClickListener listener) {
        name.setText(item.name);
        Picasso.with(itemView.getContext()).load(item.imageUrl).into(image);
        itemView.setOnClickListener(new View.OnClickListener() {
            @Override public void onClick(View v) {
                listener.onItemClick(item);
            }
        });
    }
}
}

And Use RecyclerView Adapter using below code:

recycler.setAdapter(new ContentAdapter(items, new ContentAdapter.OnItemClickListener() {
@Override public void onItemClick(ContentItem item) {
    Toast.makeText(getContext(), "Item Clicked", Toast.LENGTH_LONG).show();
}
}));

i found this from here

Hope it helped you.

You can let your Activity implements View.OnClickListener and pass it to adapter. Below is an example.

class  RAdapter extends RecyclerView.Adapter<>{
    View.OnClickListener listner;
    public RAdapter(View.OnClickListener listner) {
        this.listner = listner;
    }
    public void onBindViewHolder(MyHolder holder, final int position) {
        holder.vname.setOnClickListener(listner);

    }
}

But to handle click in Activity you will going to need clicked position. You can have it with adapter.getAdapterPosition() to validate which item is clicked.

Apart from that if you already have reference of Activity you can have OnClick inside adapter and call a public method of Activity with position to perform action.

A better way to handle clicks in ViewHolder. See the below example.

class Holder extends RecyclerView.ViewHolder implements View.OnClickListener {
        Button button;
        public Holder(View itemView) {
            super(itemView);
            button=itemView.findViewById(R.id.b1);
            button.setOnClickListener(this);
        }
        @Override
        public void onClick(View v) {
            if(v.getId()==R.id.b1){
                int position=getAdapterPosition();
                // Call a public method of Activity here 
                // with postion
            }
        }
    }

If I understood correctly you want to set the on click logic in the Activity.

You can do this by setting the OnClickListener in the Activity and passing it in the Adapter constructor.

MyAdapter myAdapter = new MyAdapter(new View.OnClickListener() {
        @Override
        public void onClick(View view) {
            Toast.makeText(activity, "clicked on " +position, Toast.LENGTH_SHORT).show();
        }
    }));

And your MyAdapter Constructor would be:

final private OnClickListener onClickListener;

public MyAdapter(OnClickListener onClickListener) {
    this.OnClickListener = OnClickListener;
}

So your new code would be something like this

public void onBindViewHolder(MyHolder holder, final int position) {
    final Listdata data = listdata.get(position);
    holder.vname.setText(data.getName());

    holder.vname.setOnClickListener(onClickListener);

}

Create an interface for the adapter class

private OnItemClickListener mListener;

public CustomAdapter(List<Listdata> listdata, OnItemClickListener listener) {
    mListener = listener;
    ...
    ...
}

private class ViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener {

    ViewHolder(View view) {
        ...
        ...
        view.setOnClickLister(this);
    }

    @override
    public void onClick(View v) {
        mListener.onAdapterItemClick(getAdapterPosition())
    }
}

interface OnItemClickListener {
    void onAdapterItemClick(int position);
}

Let the activity implement the interface

public class CustomListActivity extends AppCompatActivity implements OnItemClickListener {

...
...

@override
public void onAdapterItemClick(int position) {
    Toast.makeText(activity, "clicked on " +position, Toast.LENGTH_SHORT).show();
}

There is another way of doing this, check out this implementation

MrX

I always have one Generic Adapter in my project to avoid make a Adapter class every I use a Recyclerview. Here some example

public class AdapterRecyclerviewTextOnly extends RecyclerView.Adapter<AdapterRecyclerviewTextOnly.ViewHolder> {
private RecyclerView recyclerView;
private OnRecyclerviewListener onRecyclerviewListener;

public interface OnRecyclerviewListener {
    void onRecyclerviewBind(RecyclerView recyclerView, AdapterRecyclerviewTextOnly.ViewHolder viewHolder, int position);
    void onRecyclerviewClick(RecyclerView recyclerView, int position);
    int onItemCount(RecyclerView recyclerView);
}

public void setOnRecyclerviewListener(OnRecyclerviewListener listener) { this.onRecyclerviewListener = listener; }

public AdapterRecyclerviewTextOnly(RecyclerView recyclerView) {
    super();
    this.recyclerView = recyclerView;
}


public class ViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener {
    RecyclerView recyclerView;
    public TextView textView;

    ViewHolder(RecyclerView recyclerView, View itemView) {
        super(itemView);
        this.recyclerView = recyclerView;
        this.itemView.setOnClickListener(this);

        this.textView = itemView.findViewById(R.id.textview_title);
    }

    void onBind(int position) { onRecyclerviewListener.onRecyclerviewBind(this.recyclerView, this, position); }

    @Override
    public void onClick(View v) {
        onRecyclerviewListener.onRecyclerviewClick(this.recyclerView, getAdapterPosition());
    }
}

@Override
public AdapterRecyclerviewTextOnly.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
    View inflatedView = LayoutInflater.from(parent.getContext()).inflate(R.layout.item_recyclerview_text_only, parent, false);
    return new ViewHolder(this.recyclerView, inflatedView);
}

@Override
public void onBindViewHolder(AdapterRecyclerviewTextOnly.ViewHolder holder, int position) {
    holder.onBind(position);
}

@Override
public int getItemCount() {
    return onRecyclerviewListener.onItemCount(this.recyclerView);
}
}

And then in your Activity Class, you can use this adapter with :

    this.recyclerView = findViewById(R.id.recyclerview);
    this.recyclerView.setHasFixedSize(true);
    this.recyclerView.setLayoutManager(new LinearLayoutManager(this, LinearLayoutManager.VERTICAL, false));
    AdapterRecyclerviewTextOnly recyclerViewAdapter = new AdapterRecyclerviewTextOnly(this.recyclerView);
    this.recyclerView.setAdapter(this.recyclerViewAdapter);
    this.recyclerViewAdapter.setOnRecyclerviewListener(new AdapterRecyclerviewTextOnly.OnRecyclerviewListener() {
        @Override
        public void onRecyclerviewBind(RecyclerView recyclerView, AdapterRecyclerviewTextOnly.ViewHolder viewHolder, int position) {

        }

        @Override
        public void onRecyclerviewClick(RecyclerView recyclerView, int position) {

        }

        @Override
        public int onItemCount(RecyclerView recyclerView) { 
}
});

You can reuse this with 2 or 3 recyclerview too. First, declare a globar listener private AdapterRecyclerviewTextOnly.OnRecyclerviewListener listener;.

Then init the listener with new object then set the your every recyclerview with the listener. Use specific identifier:

if (recyclerView == recyclerViewA){ } else if (recyclerView == recyclerViewB) { } to manage your recyclerview inside the adapter.

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!