68

could anybody explain to me how to implement some of the visual touch feedback that was demonstrated at Google I/O 2014 within a CardView.

Here is how I am using the CardView in XML, there is probably something small that I am missing, so I just wondered if anyone could help me?.

<!-- A CardView -->
<android.support.v7.widget.CardView
    xmlns:card_view="http://schemas.android.com/apk/res-auto"
    android:id="@+id/CardView_1"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:layout_marginLeft="10dp"
    android:layout_marginRight="10dp"
    android:layout_marginTop="10dp" 
    card_view:cardCornerRadius="4dp"
    android:elevation="2dp">

    <LinearLayout
        android:id="@+id/LinearLayout_1"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="horizontal"
        android:onClick="RunSomeMethod"">

    <!-- Main CardView Content In Here-->

    </LinearLayout> </android.support.v7.widget.CardView>
Smiler
  • 1,316
  • 4
  • 12
  • 18

5 Answers5

185

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.

Jared Burrows
  • 54,294
  • 25
  • 151
  • 185
nhaarman
  • 98,571
  • 55
  • 246
  • 278
  • Thanks, I also had to move the 'android:onClick="RunSomeMethod"' upto the CardView, but unfortunately I am only see'ing the animation from the center of the button and not where my finger last touched... I would vote your answer up but I can't as of yet... sorry about that. – Smiler Jun 29 '14 at 10:40
  • 9
    Yes you're right. `android:clickable="true"` would work as well. As per the animation originating from the center of the button, I think that that's a bug. – nhaarman Jun 29 '14 at 10:41
  • @Smiler Google confirmed it was a bug. Edited the answer. – nhaarman Jun 30 '14 at 07:33
  • 2
    On pre-lollipop selection is shown for shadow areas as well. Is it as expected? – Eugen Martynov Nov 12 '14 at 12:10
  • 1
    As @EugenMartynov wrote, for pre-Lollipop, when pressing an item, I just see a full gray rectangle, instead of setting the color only for the card content. What should I do? – android developer Nov 17 '14 at 14:59
  • On pre-lollipop the shadow isn't generated by the framework (as it is in lollipop), but inserted around the card. And since the foreground attribute covers the whole view, it goes over the shadow as well. I have no idea how to fix this though. At least, not yet... – xip Nov 23 '14 at 22:41
  • How I set it programmatically? – Idemax Feb 07 '15 at 00:05
  • I found the answer: http://stackoverflow.com/questions/20531516/how-do-i-add-selectableitembackground-to-an-imagebutton-programatically – Idemax Feb 07 '15 at 00:14
  • 1
    To draw correctly on pre-lollipop you can use custom drawable with specific inset. See the [answer](http://stackoverflow.com/a/30192562/1317086). @MarceloFilho – GregoryK May 12 '15 at 14:15
  • hi @nhaarman I have added property to card view in XML like this. `android:clickable="true" android:foreground="?android:attr/selectableItemBackground"` but after this in my java file I have added `lvList.setOnItemClickListener(){}` but it did not catch proper touch of card view, why? – Dharmishtha Jun 08 '17 at 06:51
  • I really don't get why this is the solution ... yes it works, but feels like a hack. 1. system should automatically add ripple effect when clickable is defined, 2. ripple effect is defined by colorOnSurface https://material.io/components/cards/android#card 3. other mentions say set background .. https://stackoverflow.com/questions/57585239 which is the same bad hacky solution to me .. does anybody really UNDERSTAND why this is the solution and can explain it in manner of the material design and theme system? – childno͡.de Mar 10 '21 at 14:08
35

To draw selection on pre-Lollipop and post-Lollipop correctly you can use the following approach (the idea is to use inset drawable of selector with rounded corners for pre-Lollipop - sample below uses custom colors, you can change them to default):

android:foreground="@drawable/card_foreground"

post-Lollipop

drawable-v21/card_foreground.xml

<ripple xmlns:android="http://schemas.android.com/apk/res/android" android:color="#20000000"
        android:drawable="@drawable/card_foreground_selector" />

drawable-v21/card_foreground_selector.xml

<selector xmlns:android="http://schemas.android.com/apk/res/android">
    <item android:state_pressed="true">
        <shape android:shape="rectangle">
            <solid android:color="#18000000"/>
        </shape>
    </item>
    <item android:state_focused="true" android:state_enabled="true">
        <shape android:shape="rectangle">
            <solid android:color="#0f000000"/>
        </shape>
    </item>
</selector>

pre-Lollipop

drawable/card_foreground.xml (you'll need to tweak inset values according to elevation of your card)

<inset xmlns:android="http://schemas.android.com/apk/res/android" android:drawable="@drawable/card_foreground_selector"
    android:insetLeft="2dp"
    android:insetRight="2dp"
    android:insetTop="4dp"
    android:insetBottom="4dp"/>

drawable/card_foreground_selector.xml

<selector xmlns:android="http://schemas.android.com/apk/res/android">
    <item android:state_pressed="true">
        <shape android:shape="rectangle">
            <solid android:color="#18000000"/>
            <corners android:radius="@dimen/card_radius" />
        </shape>
    </item>
    <item android:state_focused="true" android:state_enabled="true">
        <shape android:shape="rectangle">
            <solid android:color="#0f000000"/>
            <corners android:radius="@dimen/card_radius" />
        </shape>
    </item>
</selector>
GregoryK
  • 3,011
  • 1
  • 27
  • 26
  • 4
    I think this should be the "Correct answer". It woks perfectly. Thanks! – Walter Jr. Jun 17 '15 at 14:41
  • Sadly, this affects the forefround, so it covers real content of the cardView, instead of blend in with the background of the cardView – android developer Nov 20 '15 at 00:19
  • @androiddeveloper if foreground is not what you are looking for, you might want to try: 1) have your card view below another view taking the same space but containing content 2) background with two layers: layer1 - card, layer2 - card selection – GregoryK Nov 20 '15 at 11:44
  • @Gregory I don't understand your solutions. Can you please show a sample? Maybe answer here : http://stackoverflow.com/q/33586348/878126 ? – android developer Nov 20 '15 at 14:52
  • @androiddeveloper you can try to do something like this: your content (question you mentioned is a different one) – GregoryK Nov 23 '15 at 09:09
10

This helped in my case

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"
AndyW
  • 1,431
  • 16
  • 22
3

Here is my solution. It uses ripple for lollipop+ and static foreground for pre-lollipop devices.

<android.support.v7.widget.CardView
    ...
    android:foreground="?android:attr/selectableItemBackground">
Oleksii Masnyi
  • 2,922
  • 2
  • 22
  • 27
  • 1
    isn't is better to use "?attr/selectableItemBackground" ? Also, how would you also add a checked state for it, without covering the content? – android developer Nov 20 '15 at 10:18
  • @android-developer yes, if you ask me it should and must not contain `android:` , else you will bypass attr definition from theme and applies the other answers as well – childno͡.de Mar 10 '21 at 14:10
0

Please use com.google.android.material.card.MaterialCardView in favor of androidx.cardview.widget.CardView that will give that function out of the box.

Background: https://developer.android.com/jetpack/androidx/releases/cardview is the new base superseding https://developer.android.com/topic/libraries/support-library/packages#v7-cardview and Material Components https://developer.android.com/reference#other-libraries are build on top of androidx.cardview using foreground and rippleColor as defined per https://material.io/components/cards/android#card anatomy ... so check out if you customize your ?attr/colorSurface, ?attr/colorOnSurface or app:cardForegroundColor to set matching values each other for a visible change

so, mainly this cleans up where the "hacky solution" https://stackoverflow.com/a/30192562/529977 touched the base

But: sounds like there are new issues with that like https://github.com/material-components/material-components-android/issues/1095 ‍♂️ and the code documentation seems to be a bit weired anyway https://github.com/material-components/material-components-android/blob/master/lib/java/com/google/android/material/card/MaterialCardView.java#L303

childno͡.de
  • 4,679
  • 4
  • 31
  • 57