Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Swipe and OnClick events in RecyclerView

Tags:

I'm trying to implement a swipe to dismiss action in a RecyclerView but when I set an OnClickListener on any View in a ViewHolder it overrides all OnTouch events on that view.

I can abandon OnClickListener and handle all clicks in the TouchListener but if I have multiple buttons in a child view of the RecycleView than that will be a lot of code and this doesn't look like a right way.

In my RecyleView I'm setting Swipe to dismiss listeners (similar to this):

    setOnTouchListener(touchListener);     setOnScrollListener(touchListener.makeScrollListener()); 

It works in the ListView, but in the RecycleView the OnClickListener blocks OnTouchListner events.

Example of the layout for ViewHolder view.

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" android:id="@+id/card_title" android:layout_width="match_parent" android:layout_height="wrap_content" android:minHeight="72dp" android:descendantFocusability="blocksDescendants">  <ImageView     android:id="@+id/keep_icon"     android:layout_width="48dp"     android:layout_height="48dp"     android:layout_centerInParent="true"     android:src="@drawable/ic_received" /> 

Inflating in the RecyclerView.Adapter:

@Override public ViewHolder onCreateViewHolder(ViewGroup viewGroup, int i) {     View v = mInflater.inflate(R.layout.push_card_view_compat, viewGroup, false);     return new ViewHolder(v, onClickListener, onKeepListener); } 

The ViewHolder:

public ViewHolder(final View itemView,                   final OnViewHolderClickListener onClickListener,                   final OnKeepListener onKeepListener) {     super(itemView);     keepButton = (ImageView) itemView.findViewById(R.id.keep_icon);      itemView.setOnClickListener(new View.OnClickListener() {     @Override     public void onClick(View v) {         onItemClickListener.onClick(getPosition(), itemView);     }     });     keepButton.setOnClickListener(new View.OnClickListener() {     @Override     public void onClick(View v) {         onKeepListener.onClick(getPosition(), itemView);     }     }); } 
like image 990
Ivan Fork Avatar asked Oct 29 '14 12:10

Ivan Fork


1 Answers

I achieved that by assigning OnClickListener for the buttons in the ViewHolder, and creating an interface for the touch events.

The sample project is on GitHub: https://github.com/brnunes/SwipeableRecyclerView.

In my case each item is a CardView with two buttons, and I want to detect the touch events in the CardView and the Buttons. The CardView layout looks like this:

<?xml version="1.0" encoding="utf-8"?> <android.support.v7.widget.CardView xmlns:android="http://schemas.android.com/apk/res/android"     xmlns:card_view="http://schemas.android.com/apk/res-auto"     android:layout_width="match_parent"     android:layout_height="match_parent"     android:layout_margin="5dp"     android:clickable="true"     android:foreground="?android:attr/selectableItemBackground"     android:orientation="vertical"     card_view:cardCornerRadius="5dp">      <RelativeLayout         android:layout_width="match_parent"         android:layout_height="match_parent">          <TextView             android:id="@+id/card_view_title"             android:layout_width="match_parent"             android:layout_height="50dp"             android:layout_alignParentTop="true"             android:layout_centerHorizontal="true"             android:gravity="center"             android:textColor="@android:color/black"             android:textSize="24sp" />          <LinearLayout             android:layout_width="match_parent"             android:layout_height="wrap_content"             android:layout_alignParentBottom="true"             android:layout_below="@id/card_view_title"             android:layout_centerHorizontal="true"             android:gravity="center">              <Button                 android:id="@+id/card_view_button1"                 android:layout_width="wrap_content"                 android:layout_height="wrap_content"                 android:text="Button1" />              <Button                 android:id="@+id/card_view_button2"                 android:layout_width="wrap_content"                 android:layout_height="wrap_content"                 android:text="Button2" />         </LinearLayout>     </RelativeLayout>  </android.support.v7.widget.CardView> 

Then in the Activity, I declared an interface to receive the touch events:

public interface OnItemTouchListener {     public void onCardViewTap(View view, int position);     public void onButton1Click(View view, int position);     public void onButton2Click(View view, int position); } 

And in the ViewHolder I assign OnClickListeners to the objects that I want to listen to, and call my custom listener:

public class CardViewAdapter extends RecyclerView.Adapter<CardViewAdapter.ViewHolder> {     private List<String> cards;     private OnItemTouchListener onItemTouchListener;      public CardViewAdapter(List<String> cards, OnItemTouchListener onItemTouchListener) {         this.cards = cards;         this.onItemTouchListener = onItemTouchListener;     }      @Override     public ViewHolder onCreateViewHolder(ViewGroup viewGroup, int i) {         View v = LayoutInflater.from(viewGroup.getContext()).inflate(R.layout.card_view_layout, viewGroup, false);         return new ViewHolder(v);     }      @Override     public void onBindViewHolder(ViewHolder viewHolder, int i) {         viewHolder.title.setText(cards.get(i));     }      @Override     public int getItemCount() {         return cards == null ? 0 : cards.size();     }      public class ViewHolder extends RecyclerView.ViewHolder {         private TextView title;         private Button button1;         private Button button2;          public ViewHolder(View itemView) {             super(itemView);             title = (TextView) itemView.findViewById(R.id.card_view_title);             button1 = (Button) itemView.findViewById(R.id.card_view_button1);             button2 = (Button) itemView.findViewById(R.id.card_view_button2);              button1.setOnClickListener(new View.OnClickListener() {                 @Override                 public void onClick(View v) {                     onItemTouchListener.onButton1Click(v, getPosition());                 }             });              button2.setOnClickListener(new View.OnClickListener() {                 @Override                 public void onClick(View v) {                     onItemTouchListener.onButton2Click(v, getPosition());                 }             });              itemView.setOnClickListener(new View.OnClickListener() {                 @Override                 public void onClick(View v) {                     onItemTouchListener.onCardViewTap(v, getPosition());                 }             });         }     } } 

Finally, instantiate the custom OnItemTouchListener and pass it to the CardViewAdapter constructor:

@Override protected void onCreate(Bundle savedInstanceState) {     super.onCreate(savedInstanceState);     setContentView(R.layout.activity_main);      mItems = new ArrayList<>(30);     for (int i = 0; i < 30; i++) {         mItems.add(String.format("Card number %2d", i));     }      OnItemTouchListener itemTouchListener = new OnItemTouchListener() {         @Override         public void onCardViewTap(View view, int position) {             Toast.makeText(MainActivity.this, "Tapped " + mItems.get(position), Toast.LENGTH_SHORT).show();         }          @Override         public void onButton1Click(View view, int position) {             Toast.makeText(MainActivity.this, "Clicked Button1 in " + mItems.get(position), Toast.LENGTH_SHORT).show();         }          @Override         public void onButton2Click(View view, int position) {             Toast.makeText(MainActivity.this, "Clicked Button2 in " + mItems.get(position), Toast.LENGTH_SHORT).show();         }     };      mAdapter = new CardViewAdapter(mItems, itemTouchListener);      mRecyclerView = (RecyclerView) findViewById(R.id.recycler_view);      mRecyclerView.setLayoutManager(new LinearLayoutManager(this));     mRecyclerView.setAdapter(mAdapter);      // ... Assign the swipe listener } 
like image 94
brnunes Avatar answered Oct 15 '22 08:10

brnunes