Android-L Cardview Visual Touch Feedback

Android-L CardView Visual Touch Feedback

API 11+:

Add android:foreground="?android:attr/selectableItemBackground" to your CardView element.

API 9+:

Add android:foreground="?selectableItemBackground" to your CardView element.


Edit: The ripple originating from the center and not from the touch point is a known bug, and has been fixed.

RecyclerView + CardView + Touch feedback

Background:

The CardView ignores android:background in favor of app:cardBackground which can only be color. The border and shadow are in fact part of the background so you cannot set your own.

Solution:

Make the layout inside the CardView clickable instead of the card itself. You already wrote both attributes needed for this layout:

android:clickable="true"
android:background="?android:selectableItemBackground"

CardView lift on touch

Try wrapping the animator resource with <set>.

<?xml version="1.0" encoding="utf-8"?>
<set>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item
android:state_enabled="true"
android:state_pressed="true">
<set>
<objectAnimator
android:duration="@android:integer/config_shortAnimTime"
android:propertyName="translationZ"
android:valueTo="6dp"
android:valueType="floatType" />
</set>
</item>
<item>
<set>
<objectAnimator
android:duration="@android:integer/config_shortAnimTime"
android:propertyName="translationZ"
android:valueTo="0"
android:valueType="floatType" />
</set>
</item>
</selector>
</set>

CardView's background which will respond to android:state_selected and android:state_pressed

After experimenting for quite some time, I can pretty much conclude that this is limitation of current CardView - https://code.google.com/p/android/issues/detail?id=78198

Don't use CardView's foreground workaround. Although it is widely being proposed, it just don't work!

My suggestion is, avoid using CardView if you need a customized selector. Replace it LayerDrawable. Here's what I had done.

card.xml

<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
<item>
<shape>
<padding android:top="1dp" android:right="1dp" android:bottom="1dp" android:left="1dp" />
<solid android:color="@android:color/transparent" />
<stroke
android:width="1dp"
android:color="#ffededed" />
<corners android:radius="2dp" />
</shape>
</item>

<item>
<shape>
<padding android:top="1dp" android:right="1dp" android:bottom="1dp" android:left="1dp" />
<stroke
android:width="1dp"
android:color="#ffe8e8e8" />
<corners android:radius="2dp" />
</shape>
</item>

<item>
<shape>
<padding android:top="0dp" android:right="1dp" android:bottom="1dp" android:left="1dp" />
<stroke
android:width="1dp"
android:color="#ffe1e1e1" />
<corners android:radius="2dp" />
</shape>
</item>

<item>
<shape>
<padding android:top="0dp" android:right="0dp" android:bottom="1dp" android:left="0dp" />
<stroke
android:width="1dp"
android:color="#ffdbdbdb" />
<corners android:radius="2dp" />
</shape>
</item>

<item>
<shape>
<padding android:top="0dp" android:right="0dp" android:bottom="1dp" android:left="0dp" />
<stroke
android:width="1dp"
android:color="#ffd5d5d5" />
<corners android:radius="2dp" />
</shape>
</item>

<!--
<item>
<shape>
<padding android:top="0dp" android:right="0dp" android:bottom="1dp" android:left="0dp" />
<stroke
android:width="1dp"
android:color="#ffcfcfcf" />
<corners android:radius="2dp" />
</shape>
</item>
-->

<item>
<shape >
<solid android:color="#ffffffff" />
<corners android:radius="2dp" />
</shape>
</item>
</layer-list>

card_selected.xml

<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
<item>
<shape>
<padding android:top="1dp" android:right="1dp" android:bottom="1dp" android:left="1dp" />
<stroke
android:width="1dp"
android:color="#ffededed" />
<corners android:radius="2dp" />
</shape>
</item>

<item>
<shape>
<padding android:top="1dp" android:right="1dp" android:bottom="1dp" android:left="1dp" />
<stroke
android:width="1dp"
android:color="#ffe8e8e8" />
<corners android:radius="2dp" />
</shape>
</item>

<item>
<shape>
<padding android:top="0dp" android:right="1dp" android:bottom="1dp" android:left="1dp" />
<stroke
android:width="1dp"
android:color="#ffe1e1e1" />
<corners android:radius="2dp" />
</shape>
</item>

<item>
<shape>
<padding android:top="0dp" android:right="0dp" android:bottom="1dp" android:left="0dp" />
<stroke
android:width="1dp"
android:color="#ffdbdbdb" />
<corners android:radius="2dp" />
</shape>
</item>

<item>
<shape>
<padding android:top="0dp" android:right="0dp" android:bottom="1dp" android:left="0dp" />
<stroke
android:width="1dp"
android:color="#ffd5d5d5" />
<corners android:radius="2dp" />
</shape>
</item>

<!--
<item>
<shape>
<padding android:top="0dp" android:right="0dp" android:bottom="1dp" android:left="0dp" />
<stroke
android:width="1dp"
android:color="#ffcfcfcf" />
<corners android:radius="2dp" />
</shape>
</item>
-->

<item>
<shape>
<solid android:color="#ffffe1b3" />
<stroke
android:width="1px"
android:color="#fff76d3c" />
<corners android:radius="2dp" />
</shape>
</item>
</layer-list>

statelist_item_background.xml

<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android"
android:exitFadeDuration="@android:integer/config_mediumAnimTime">

<item android:state_pressed="true" android:drawable="@drawable/card_selected" /> <!-- pressed -->
<item android:state_selected="true" android:drawable="@drawable/card_selected" /> <!-- pressed -->

<item android:drawable="@drawable/card" />

</selector>

layout.xml

<!-- A CardView that contains a TextView -->

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:padding="10dp"
android:layout_marginLeft="5dp"
android:layout_marginRight="5dp"
android:layout_marginTop="5dp"
android:layout_marginBottom="5dp"
android:background="@drawable/statelist_item_background"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:clickable="true" >

<TextView
android:id="@+id/txt_label_item"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textAppearance="?android:attr/textAppearanceLarge"
tools:text="Item Number One" />

<TextView
android:id="@+id/txt_date_time"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textAppearance="?android:attr/textAppearanceMedium"
tools:text="Item Number One" />
</LinearLayout>

You will get the pretty nice outcome.

Sample Image

Ripple Effect on androidx.cardview.widget.CardView

Don't use any background/foreground in CardView. If you use any background color then, then just add app:cardBackgroundColor="@color/cardBackgroundColor. Remove any padding from the CardView. Use margin for space between items.

Now, for the ripple effect in CardView, just add an immediate child layout in your CardView. Set android:background="?attr/selectableItemBackground" in the child layout. Add any necessary padding/margin in the child if you need.

<?xml version="1.0" encoding="utf-8"?>
<androidx.cardview.widget.CardView xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:card_view="http://schemas.android.com/apk/res-auto"
xmlns:app="http://schemas.android.com/tools"
android:id="@+id/card_view"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:clickable="true"
card_view:cardCornerRadius="6dp"
card_view:cardElevation="4dp"
card_view:cardMaxElevation="6dp"
app:ignore="NamespaceTypo">

<!-- Child Layout -->
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="5dp"
android:background="?attr/selectableItemBackground"
android:orientation="vertical">

<!-- Your content here -->
</androidx.constraintlayout.widget.ConstraintLayout>
</androidx.cardview.widget.CardView>

Making entire cardview clickable

Just a thought if more elegant solutions could not be found: you may try overriding onInterceptTouchEvent() for the cardview and use a gesture detector which listens for a tap up on the motion event intercepted. Return true if the gesture is detected, such that the MotionEvent is intercepted and not passed on to its children, including the listview.

This way the cardview could see the click event before the listview.

Add selectableItemBackground to CardView

The issue was that I used the same Drawable with multiple Cards.

You have to retrieve a new Drawable for each Card.

More details in this answer.



Related Topics



Leave a reply



Submit