CardView background color states not being respected

AndroidAndroid Cardview

Android Problem Overview


In brief:

How can we define color states of CardView's cardBackgroundColor property (in a ListView layout, in this case)?

(I am using RC1 of Android L developer preview, on a phone with 4.4 installed, and "com.android.support:cardview-v7:21.0.0-rc1" in build.gradle)

Longer:

In CardView layout, we set the corner radius and background color of the CardView via cardCornerRadius and cardBackgroundColor.

However, the background color doesn't repect selected states, i.e. if the list item is pressed, for example.

If, in the inner view of the CardView, you set a background colour, and associated states, which are respected, however, it will display over the corners you defined in the CardView.

So, how can we ensure the states in CardView's cardBackgroundColor are respected?

Here's the color used for the cardBackgroundColor, colour_with_states.xml:

<selector xmlns:android="http://schemas.android.com/apk/res/android">
    <item android:state_focused="true"  android:state_enabled="false" android:state_pressed="true" android:color="@android:color/holo_green_dark" />
    <item android:state_focused="true"  android:state_enabled="false"                              android:color="@android:color/holo_green_dark" />
    <item android:state_focused="true"                                android:state_pressed="true" android:color="@android:color/holo_green_dark" />
    <item android:state_focused="false"                               android:state_pressed="true" android:color="@android:color/holo_green_dark" />
    <item android:state_focused="true"                                                             android:color="@android:color/holo_green_dark" />
    <!-- Only this below is seen in the cardview dispaly -->
    <item android:color="@android:color/holo_blue_bright" />
</selector>

And the layout that uses the CardView:

<android.support.v7.widget.CardView
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:cardview="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    cardview:cardCornerRadius="10dp"
    cardview:cardBackgroundColor="@color/colour_with_states"
    >

<!-- If we set a background color below, it will overwrite our radius defined above -->
<TextView
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    tools:text="Lorem ipsum"
    android:id="@android:id/text1"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:textAppearance="?android:attr/textAppearanceListItem"
    android:background="@null"
    android:gravity="center_vertical"
    android:paddingTop="8dip"
    android:paddingBottom="8dip"
    android:paddingStart="8dip"
    android:paddingEnd="8dip"
    />

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

Android Solutions


Solution 1 - Android

Though this is not ideal, since the edges are not rounded, you can add touch feedback to a CardView like this :

<android.support.v7.widget.CardView
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    app:cardCornerRadius="4dp"
    android:clickable="true"
    android:foreground="?android:attr/selectableItemBackground">

    //Nested View ...

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

Adding the android:foreground and android:clickable attributes to the CardView.

Also this has a negative side effect in that the android:clickable attribute overrides any clickListener, and therefore those clickListeners don't get triggered.

Update

I have some examples of CardView implementations

Loop (https://github.com/lawloretienne/Loop) - https://github.com/lawloretienne/Loop/blob/master/app/src/main/res/layout/category_card.xml

QuickReturn (https://github.com/lawloretienne/QuickReturn) - https://github.com/lawloretienne/QuickReturn/blob/master/sample/src/main/res/layout/activity_quick_return.xml

Update 2

After more research I have come up with a good solution for CardViews on all API versions including pre-Lollipop.

https://medium.com/@etiennelawlor/layout-tips-for-pre-and-post-lollipop-bcb2e4cdd6b2#.9h0v1gmaw

Solution 2 - Android

Sometimes, you may want the CardView to have visual touch feedback. The android:foreground="?android:attr/selectableItemBackground" solution is perfect for this.

However, you may consider using drawSelectorOnTop(true) with your ListView. This will require no change on your CardView at all.

Let me know if further clarification is needed.

Solution 3 - Android

Here's my way to solve your problem.

First, create a custom class named CustomCardView extends CardView

Then override the drawableStateChanged() method, change the card background color by call setCardBackgroundColor() method when card's press status changed.

Last, replace the CardView with this CustomCardView in you layout file.

the only one disadvantage of this solution is cardview can't display ripple press effect on Android 5.0 and above.

here's my code:

public class CustomCardView extends CardView {

public CustomCardView(Context context) {
	super(context);
	// TODO Auto-generated constructor stub
}

public CustomCardView(Context context, AttributeSet attrs) {
	super(context, attrs);
	// TODO Auto-generated constructor stub
}

public CustomCardView(Context context, AttributeSet attrs, int defStyleAttr) {
	super(context, attrs, defStyleAttr);
	// TODO Auto-generated constructor stub
}

@Override
protected void drawableStateChanged() {
    super.drawableStateChanged();
	if (isPressed()) {
		this.setCardBackgroundColor(getContext().getResources().getColor(R.color.card_view_pressed));
	} else {
		this.setCardBackgroundColor(getContext().getResources().getColor(R.color.card_view_normal));
	}
}

}

Solution 4 - Android

One workaround which I used was to do the UI changes programmatically by overriding the View.OnTouchListener OnTouch() event handler within my custom ViewHolder.

@Override
public boolean onTouch (View v, MotionEvent event) 
{
	int action = event.getActionMasked();
	if (action == MotionEvent.ACTION_DOWN) 
	{
		mCardView.setCardBackgroundColor(STATE_PRESSED_COLOR);
	} 
	else if (action == MotionEvent.ACTION_UP || action == MotionEvent.ACTION_CANCEL) 
	{
		mCardView.setCardBackgroundColor(DEFAULT_COLOR);
	}
	return false;
}

Solution 5 - Android

**Just add there lines inside card view **

    android:clickable="true"
    android:focusableInTouchMode="true"
    android:foreground="?android:attr/selectableItemBackground"

Solution 6 - Android

You can use color state in code and it work perfectly.

cardView.setCardBackgroundColor(context.getColorStateList(R.color.card_view_selectable))

in color > card_view_selectable.xml

<selector xmlns:android="http://schemas.android.com/apk/res/android">
    <item android:color="#4fc3f7" android:state_activated="true" />
    <item android:color="#fff" />
</selector>

Then use isActivated cause background color changed to state_activated or default.

cardView.isActivated = true    // or false

Solution 7 - Android

I used a rectangle shape with the same corner radius as the cardview. And then used the xml drawable as a background for the inner view of the cardview. The background does not display over cardview corner, though i still get a small padding between the card and its inner view.

enter image description here

Solution 8 - Android

If you look at the definition of carBackgroundColor property, at least in android support library it is:

<resources>
    <declare-styleable name="CardView">
        <!-- Background color for CardView. -->
        <attr name="cardBackgroundColor" format="color" />
    </declare-styleable>
</resource>

Here it says it takes only color for cardBackgroundValue. I guess this means selector is not respected but falls through to the default value ie. color at the bottom of your selector.

Solution 9 - Android

Use android:foreground instead of android:background in your <CardView/>. below are sample code of a CardView .

 <android.support.v7.widget.CardView
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:clickable="true"
        android:foreground="?android:attr/selectableItemBackground"
        app:cardCornerRadius="2dp"
        app:cardElevation="2dp">

// others view component 

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

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
Questionmmm111mmmView Question on Stackoverflow
Solution 1 - AndroidEtienne LawlorView Answer on Stackoverflow
Solution 2 - AndroidAnthony ChuinardView Answer on Stackoverflow
Solution 3 - AndroidLoyeaView Answer on Stackoverflow
Solution 4 - AndroidweiyView Answer on Stackoverflow
Solution 5 - AndroidPrashant Maheshwari AndroView Answer on Stackoverflow
Solution 6 - AndroidMohammad Reza KhahaniView Answer on Stackoverflow
Solution 7 - AndroidHasan HashemView Answer on Stackoverflow
Solution 8 - Androiduser6694745View Answer on Stackoverflow
Solution 9 - AndroidPiash SarkerView Answer on Stackoverflow