12

In the latest Android SDK we now have the new CardView, I have replaced my old CustomCardView with the new version, but when running with this on older versions of Android I see that the state_pressed & state_focused are ugly squares which show up above the CardView...

does anyone know how I could emulate the following in the new CardView but only when using older versions of Android?

<selector xmlns:android="http://schemas.android.com/apk/res/android"
    android:enterFadeDuration="150"
    android:exitFadeDuration="150" >

    <item android:state_pressed="true"
          android:drawable="@drawable/card_on_press"/>
    <item android:state_focused="true" android:state_enabled="true"
          android:drawable="@drawable/card_on_focus"/>
    <item android:state_enabled="true"
          android:drawable="@drawable/card_default"/>
    <item android:state_enabled="false"
          android:drawable="@drawable/card_on_press"/>

</selector>

And for those of you interested here is the CardView that I am using now:

<android.support.v7.widget.CardView
    xmlns:card_view="http://schemas.android.com/apk/res-auto"
    android:id="@+id/CardView"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:layout_marginLeft="10dp"
    android:layout_marginRight="10dp"
    android:layout_marginTop="10dp"
    android:foreground="?android:attr/selectableItemBackground"
    android:onClick="RunSomeMethod"
    card_view:cardCornerRadius="4dp"
    android:focusable="true"
    android:elevation="2dp">

    <LinearLayout
        android:id="@+id/LinearLayout"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="horizontal"
        android:focusable="false"
        android:visibility="visible">

    </LinearLayout>
</android.support.v7.widget.CardView>
SilentKiller
  • 6,944
  • 6
  • 40
  • 75
Smiler
  • 1,316
  • 4
  • 12
  • 18

3 Answers3

14

Fixed with the following code:

values-v21\styles.xml:

<style name="CardViewStyle" parent="CardView.Light">
    <item name="android:foreground">?android:attr/selectableItemBackground</item>
</style>

values\styles.xml:

<style name="CardViewStyle" parent="android:Widget.TextView">
    <item name="android:foreground">@drawable/card</item>
</style>

A card from layout\main_layout.xml:

<android.support.v7.widget.CardView
    xmlns:card_view="http://schemas.android.com/apk/res-auto"
    android:id="@+id/Card_View"
    style="@style/CardViewStyle"
    android:layout_width="480dp"
    android:layout_height="wrap_content"
    android:layout_gravity="center_horizontal"
    android:layout_marginLeft="10dp"
    android:layout_marginRight="10dp"
    android:layout_marginTop="10dp"
    card_view:cardCornerRadius="4dp"
    android:elevation="2dp">

This will allow you to offer the animations in v21+ but also offer alternative animations in pre-v21 rather than the big blue/grey square.

Here is the card.xml I'm using as a drawable for those of you interested:

<selector xmlns:android="http://schemas.android.com/apk/res/android"
    android:enterFadeDuration="150"
    android:exitFadeDuration="150" >

    <item android:state_pressed="true"
          android:drawable="@drawable/card_on_press"/>
    <item android:state_focused="true" android:state_enabled="true"
          android:drawable="@drawable/card_on_focus"/>
    <item android:state_enabled="true"
          android:drawable="@drawable/card_default"/>
    <item android:state_enabled="false"
          android:drawable="@drawable/card_on_press"/>
</selector>

Note: Drawable default will be fine as a transparent item as CardView provides a default background for all android versions.

SilentKiller
  • 6,944
  • 6
  • 40
  • 75
Smiler
  • 1,316
  • 4
  • 12
  • 18
  • Where do you generate the assets : @drawable/card_on_press, @drawable/card_default, @drawable/card_on_focus, @drawable/card_on_press What do they look like? – Etienne Lawlor Sep 15 '14 at 21:03
  • I created an xml file for each which contains something similar to the code below – Smiler Sep 16 '14 at 21:26
  • 1
    Sorry I couldn't edit my post, I have created another post on here containing the card_on_press.xml file code, its just the same for each but the colors are different in each of them, card_default would be transparent(no color at all), but the others would be a slightly darker color, you can create those colors in the colors.xml file and adjust the color names in the code above as required. – Smiler Sep 16 '14 at 21:47
  • The only way i can see the selector working is if i add the attribute `android:clickable="true"` to the `CardView` element. But if I do that it overrides the clicklisteners I have set up. How do you get the clicklisteners and the selectors to work at the same time? – Etienne Lawlor Sep 17 '14 at 00:41
  • Add android:onClick="RunSomeMethod"to just the CardView and remove anything like that from the cards child views. – Smiler Sep 17 '14 at 07:36
  • For some reason, I get "NoClassDefFoundError: android.support.v7.cardview.R$styleable " . Is it possible it's no longer possible to set the style of the CardView ? Can you please post a sample project to Github ? – android developer Nov 18 '14 at 09:23
4

Maybe the library has been updated but when I use the following it looks fine on older versions of Android.

You can use the 'CardView.Dark' and CardView.Light' (will auto-generate with the card support library)

In your values/styles.xml

<style name="CardViewStyle.Light" parent="CardView.Light">
    <item name="android:foreground">?android:attr/selectableItemBackground</item>
</style>

<style name="CardViewStyle.Dark" parent="CardView.Dark">
    <item name="android:foreground">?android:attr/selectableItemBackground</item>
</style>

Layouts for CardView

Create two layouts, one for dark and one for light. They will include the card views like this:

<android.support.v7.widget.CardView
        android:id="@+id/my_card_view"
        style="@style/CardViewStyle.Light"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_gravity="center_horizontal"
        android:orientation="vertical"
        android:layout_marginLeft="8dp"
        android:layout_marginRight="8dp"
        android:layout_marginBottom="8dp"
        app:cardCornerRadius="2dp"
        android:elevation="2dp">

    ... other layout stuff ...

</android.support.v7.widget.CardView>

<android.support.v7.widget.CardView
        android:id="@+id/my_card_view"
        style="@style/CardViewStyle.Dark"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_gravity="center_horizontal"
        android:orientation="vertical"
        android:layout_marginLeft="8dp"
        android:layout_marginRight="8dp"
        android:layout_marginBottom="8dp"
        app:cardCornerRadius="2dp"
        android:elevation="2dp">

    ... other layout stuff ...

</android.support.v7.widget.CardView>

Take note to switching the style in each CardView.

Other customizations

If you want to customize the color rather than just having black or white, you can use the following:

To get the CardView drawable you can use:

View cardView = findViewById(R.id.my_card_view);
Drawable cardViewDrawable cardView.getBackground();
convertIcon(setViewBackground(cardView, cardViewDrawable));

My 'convertIcon' is simply the following:

/**
 *
 * @param drawable e.g. "getResources().getDrawable(R.drawable.my_drawable)"
 * @param tint e.g. "Color.parseColor(lightTitlebarFg)"
 * @return Drawable
 *
 */
public static Drawable convertIcon(Drawable drawable, int tint) {
    ColorFilter filter = new PorterDuffColorFilter(tint, PorterDuff.Mode.SRC_ATOP);
    drawable.setColorFilter(filter);
    return drawable;
}

My 'setViewBackground' is simply the following:

@TargetApi(Build.VERSION_CODES.JELLY_BEAN)
@SuppressWarnings("deprecation")
public static void setViewBackground(View view, Drawable drawable){
    if (UIUtils.isJB()) {
        view.setBackground(drawable);
    } else {
        view.setBackgroundDrawable(drawable);
    }
}

StateListDrawable

If you want to create a StateListDrawable programmatically can use the following:

StateListDrawable states = new StateListDrawable();
states.addState(new int[] { android.R.attr.state_focused }, focusedDrawable);
states.addState(new int[] { android.R.attr.state_activated }, activatedDrawable);
states.addState(new int[] { android.R.attr.state_enabled }, defaultDrawable);
states.addState(new int[] {}, defaultDrawable);
Codeversed
  • 9,287
  • 3
  • 43
  • 42
3

Here is the code that I used in the card_on_press.xml file which is stored in the drawable folder of the project:

<item>
    <shape>
        <padding
            android:bottom="0dp"
            android:left="0dp"
            android:right="0dp"
            android:top="0dp" />
        <solid android:color="@color/transparent" />
    </shape>
</item>

<item>
    <shape>
        <padding
            android:bottom="0dp"
            android:left="0dp"
            android:right="0dp"
            android:top="0dp" />
        <solid android:color="@color/card_shadow_1_on_press" />
        <corners android:radius="8dp" />
    </shape>
</item>
<item>
    <shape>
        <padding
            android:bottom="0dp"
            android:left="0dp"
            android:right="0dp"
            android:top="0dp" />
        <solid android:color="@color/card_shadow_2_on_press" />
        <corners android:radius="8dp" />
    </shape>
</item>
<!-- Background -->
<item>
    <shape>
        <solid android:color="@color/card_background_on_press" />
        <corners android:radius="8dp" />
    </shape>
</item>

Smiler
  • 1,316
  • 4
  • 12
  • 18
  • Very interesting, but something is wrong with this draw able XML, it seems to be incomplete, please could you post the entire file? Thanks! – Lisa Anne Sep 27 '14 at 16:17
  • Hi Smiler, Please what do you use the background for? Thanks! – Lisa Anne Sep 27 '14 at 16:39
  • 1
    I use 'android.support.v7.widget.CardView' as the parent view to give it the default cardview bg and the alt bg's are only required for older android versions to make them appear darker when pressed, the rest is still done by the cardview even on older android versions. you will need to copy this file and rename it for each state ie: on press/default/on focus etc. the purpose of this is to allow you to round off the corners of the darker overlay to make it look like it is just the cardview and not an overlay. sorry if I haven't explained it well enough... – Smiler Sep 28 '14 at 21:49
  • Also most of the other posts on here contain the required code to create the desired result. – Smiler Sep 28 '14 at 21:51
  • Please don't post sections of same answer as different answers. It's very confusing to other users. – Binoy Babu Oct 24 '14 at 06:22
  • 1
    My apologies for the confusion. – Smiler Oct 24 '14 at 13:06