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); } }); }
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 Button
s. 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 }
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With