Touch feedback with RecyclerView and CardView
AndroidAndroid RecyclerviewAndroid CardviewRippledrawableTouch FeedbackAndroid Problem Overview
I would love to enable touch feedback for my Open-Source library.
I've created a RecyclerView
and a CardView
. The CardView
contains different areas with different onClick
actions. Now I would love to trigger the Ripple effect if a user clicks anywhere in the card, but I'm not able to achieve this behavior.
This is my listitem, You can find it on GitHub too: https://github.com/mikepenz/AboutLibraries/blob/master/library/src/main/res/layout/listitem_opensource.xml
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:clickable="true"
android:background="@drawable/button_rect_normal"/>
<LinearLayout
android:padding="6dp"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<LinearLayout
android:gravity="center_vertical"
android:paddingLeft="8dp"
android:paddingRight="8dp"
android:orientation="horizontal"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<TextView
android:id="@+id/libraryName"
android:textColor="@color/title_openSource"
android:textSize="@dimen/textSizeLarge_openSource"
android:textStyle="normal"
android:layout_width="0dp"
android:layout_weight="5"
android:layout_height="wrap_content"
android:ellipsize="end"
android:maxLines="1"/>
<TextView
android:id="@+id/libraryCreator"
android:textColor="@color/text_openSource"
android:textStyle="normal"
android:layout_width="0dp"
android:layout_weight="2"
android:layout_height="wrap_content"
android:layout_marginTop="2dp"
android:gravity="right"
android:maxLines="2"
android:textSize="@dimen/textSizeSmall_openSource"/>
</LinearLayout>
<View
android:layout_width="match_parent"
android:layout_height="1px"
android:layout_marginTop="4dp"
android:background="@color/dividerLight_openSource"/>
<TextView
android:id="@+id/libraryDescription"
android:textSize="@dimen/textSizeSmall_openSource"
android:textStyle="normal"
android:textColor="@color/text_openSource"
android:padding="8dp"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:maxLines="20">
</TextView>
<View
android:id="@+id/libraryBottomDivider"
android:layout_width="match_parent"
android:layout_height="1px"
android:layout_marginTop="4dp"
android:background="@color/dividerLight_openSource"/>
<LinearLayout
android:id="@+id/libraryBottomContainer"
android:gravity="center_vertical"
android:paddingLeft="8dp"
android:paddingTop="4dp"
android:paddingRight="8dp"
android:orientation="horizontal"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<TextView
android:id="@+id/libraryVersion"
android:textColor="@color/text_openSource"
android:textStyle="normal"
android:layout_width="0dp"
android:layout_weight="1"
android:layout_height="wrap_content"
android:gravity="left"
android:maxLines="1"
android:textSize="@dimen/textSizeSmall_openSource"/>
<TextView
android:id="@+id/libraryLicense"
android:textColor="@color/text_openSource"
android:textStyle="normal"
android:layout_width="0dp"
android:layout_weight="1"
android:layout_height="wrap_content"
android:gravity="right"
android:maxLines="1"
android:textSize="@dimen/textSizeSmall_openSource"/>
</LinearLayout>
</LinearLayout>
And the onClick
is set on 3 parts of this layout. libraryCreator
, libraryDescription
, and libraryBottomContainer
.
I hope someone got an idea what is going wrong here.
Thanks for your help.
Android Solutions
Solution 1 - Android
Assuming you are using Material/Appcompat theme and Lollipop,I got this to work by making the CardView have the following attributes:
android:focusable="true"
android:clickable="true"
android:foreground="?android:attr/selectableItemBackground"
Solution 2 - Android
CardLayout is just a subclass of ViewGroup, So setting:
android:background="?android:attr/selectableItemBackground"
should do the trick.
UPDATE:
(looks like you are trying to use a custom ripple color.)
Try using a ripple drawable with custom color as the background (I haven't used this, So I cannot verify this behavior.) see: the documentation or this question to get you started.
Solution 3 - Android
for me:
android:background="?android:attr/selectableItemBackground"
made it finally work. see background of recyclerView / content behind the item, and also see the ripple effect used with recyclerView.
Solution 4 - Android
None of the above answers worked for me or was either too complicated.
Then I realized I had to set the following properties in THE RECYCLERVIEW ADAPTER LAYOUT.
android:background="?android:attr/selectableItemBackground"
android:focusable="true"
android:clickable="true"
Then I got it to work.
Solution 5 - Android
I was now able to solve the issue.
The problem is that i use onClickListener on the TextViews and this click listener prevents the RippleForeground from being notified about the touch event. So the solution is to implement an TouchListener on those TextViews and pass through the touch event.
The class is really simple, and can be used everywhere:
package com.mikepenz.aboutlibraries.util;
import android.support.v7.widget.CardView;
import android.view.MotionEvent;
import android.view.View;
/**
* Created by mikepenz on 16.04.15.
*/
public class RippleForegroundListener implements View.OnTouchListener {
CardView cardView;
public RippleForegroundListener setCardView(CardView cardView) {
this.cardView = cardView;
return this;
}
@Override
public boolean onTouch(View v, MotionEvent event) {
// Convert to card view coordinates. Assumes the host view is
// a direct child and the card view is not scrollable.
float x = event.getX() + v.getLeft();
float y = event.getY() + v.getTop();
if (android.os.Build.VERSION.SDK_INT >= 21) {
// Simulate motion on the card view.
cardView.drawableHotspotChanged(x, y);
}
// Simulate pressed state on the card view.
switch (event.getActionMasked()) {
case MotionEvent.ACTION_DOWN:
cardView.setPressed(true);
break;
case MotionEvent.ACTION_UP:
case MotionEvent.ACTION_CANCEL:
cardView.setPressed(false);
break;
}
// Pass all events through to the host view.
return false;
}
}
It can be used by adding this as TouchListener:
RippleForegroundListener rippleForegroundListener = new RippleForegroundListener();
older.libraryCreator.setOnTouchListener(rippleForegroundListener);
This listener will just pass through the touch event to the CardView and trigger the correct Ripple effect. (You can also modify this to take any view. It should not be limited to a CardView)
Solution 6 - Android
android:foreground="?android:attr/selectableItemBackground"
If you use foreground instead background, you don't need to use clickable or focusable
Solution 7 - Android
In my case, i was need to show custom background and ripple effect as well. So the way I achieved this is by applying these two attributes on the root layout of my recycler-view item:
android:background="@drawable/custom_bg"
android:foreground="?android:attr/selectableItemBackground"
Solution 8 - Android
Had the same problem as Jiang:
I have to set the attributes to the layout-file, which is inflating the RecycleView.
My Main-Layout: (Don't add the properties here!)
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent">
<android.support.v7.widget.RecyclerView
android:id="@+id/recycler_view"
android:scrollbars="vertical"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
</RelativeLayout>
My RecycleViewAdapter:
View v = inflater.inflate(R.layout.list_center_item, parent, false);
My list_center_item.xml (Add the properties here!)
<?xml version="1.0" encoding="utf-8"?>
<TextView xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@android:id/text1"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:gravity="center"
android:background="?android:attr/selectableItemBackground"
android:clickable="true"
android:focusable="true"
/>
Solution 9 - Android
You have to add following lines in the layout that you are using under CardView
android:clickable="true"
android:focusable="true"
android:background="@drawable/my_ripple"
Example
<android.support.v7.widget.CardView android:layout_height="wrap_content"
android:layout_width="match_parent"
xmlns:android="http://schemas.android.com/apk/res/android">
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:clickable="true"
android:focusable="true"
android:background="@drawable/my_ripple">
Solution 10 - Android
The actual silly issue was in my case is the following....
Technically, It's not the case just like everyone suggests here in my case. The real issue is you should not be using android:clickable="true" uncesseriliy everywhere in the LayoutView
, RelativeView
or TextView
.
It should only be in CardView. Then it works like charm
android:focusable="true"
Is not really requied.
Just add the following in your CardView
android:clickable="true" android:foreground="?android:attr/selectableItemBackground"
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout 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="wrap_content">
<android.support.v7.widget.CardView
android:id="@+id/card_view"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_gravity="center"
android:layout_margin="@dimen/card_margin"
android:foreground="?android:attr/selectableItemBackground"
android:clickable="true"
android:elevation="3dp"
card_view:cardCornerRadius="@dimen/card_corner_radius">
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<ImageView
android:id="@+id/card_template_item_image"
android:layout_width="match_parent"
android:layout_height="@dimen/card_size_height"
android:scaleType="fitXY" />
<TextView
android:id="@+id/card_template_item_title"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_below="@id/card_template_item_image"
android:paddingLeft="@dimen/card_title_padding"
android:paddingRight="@dimen/card_title_padding"
android:paddingTop="@dimen/card_title_padding"
android:textSize="@dimen/card_title_size"
android:text="@string/place_holder_item_name"/>
<TextView
android:id="@+id/card_template_item_sub_title"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_below="@id/card_template_item_title"
android:paddingBottom="@dimen/card_sub_title_padding"
android:paddingLeft="@dimen/card_sub_title_padding"
android:paddingRight="@dimen/card_sub_title_padding"
android:textSize="@dimen/card_sub_title_size"
android:text="@string/place_holder_item_category"/>
</RelativeLayout>
</android.support.v7.widget.CardView>
</LinearLayout>