Attach custom CardView style to theme
Asked Answered
U

4

24

In my app, I have two themes (light and dark), and I want all of my CardViews to change their background color depending on which theme is selected.

What I don't want is:

<android.support.v7.widget.CardView
    style="@style/CardView.MyBlue"
    android:layout_width="200dp"
    android:layout_height="100dp"
    android:layout_gravity="center_horizontal">

The above code is not dynamic. I need my two styles to automatically be applied depending on if a light or a dark theme is selected.


What I have right now doesn't work:

<style name="AppTheme.Light" parent="Theme.AppCompat.Light">
   ...
   <item name="cardViewStyle">@style/CardViewStyle.Light</item>
</style>
<style name="AppTheme.Dark" parent="Theme.AppCompat">
   ...
   <item name="cardViewStyle">@style/CardViewStyle.Dark</item>
</style>

<style name="CardViewStyle.Light" parent="CardView">
    <item name="cardBackgroundColor">@color/cardview_dark_background</item>
</style>

<style name="CardViewStyle.Dark" parent="CardView">
        <item name="cardBackgroundColor">@color/cardview_light_background</item>
</style>

I read somewhere that you can define a styleable.xml file in /res/values/ to key the word cardViewStyle, so I did that:

styleable.xml:

<resources>
    <declare-styleable name="AppTheme">
        <attr name="cardViewStyle" format="reference" />
    </declare-styleable>
</resources>

Update

Similar question here with no answer either.

Unsure answered 23/5, 2015 at 21:37 Comment(0)
B
21

In styles.xml

<resources>

    <attr format="reference" name="cardStyle"/>

    <style name="Light" parent="Theme.AppCompat.NoActionBar">
        <item name="cardStyle">@style/CardView.Light</item>
        ...
    </style>

    <style name="Dark" parent="Theme.AppCompat.NoActionBar">
        <item name="cardStyle">@style/CardView.Dark</item>
        ...
    </style>
</resources>

Then in your other xml to use the new attribute, you would use it like this

style="?attr/cardStyle"
Beckon answered 18/6, 2015 at 1:43 Comment(1)
works like a charm! Make sure you put CardView.Light in your light style so that it picks it up when you switch back and forthHuang
T
10

To show more detail about how to implement David Park's solution...

Put this in attrs.xml:

<declare-styleable name = "cardStyle">
     <attr name="cardStyle" format="reference" />
</declare-styleable>

<style name="Light" parent="Theme.AppCompat.NoActionBar">
    <item name="cardStyle">@style/CardView.Light</item>
</style>

<style name="Dark" parent="Theme.AppCompat.NoActionBar">
    <item name="cardStyle">@style/CardView.Dark</item>
</style>

Then, add cardStyle to your theme in styles.xml:

<style name="AppThemeLight" parent="AppTheme.Base"/>
<style name="AppTheme.Base" parent="Theme.AppCompat.Light.NoActionBar">
    <item name="cardStyle">@style/CardView.Light</item>
</style>

<style name="AppThemeDark" parent="AppTheme.Base.Dark"/>
<style name="AppTheme.Base.Dark" parent="Theme.AppCompat.NoActionBar">
    <item name="cardStyle">@style/CardView.Dark</item>
</style>

Then use the attr wherever you have a CardView:

<android.support.v7.widget.CardView xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:card_view="http://schemas.android.com/apk/res-auto"
    android:id="@+id/header_card"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    card_view:cardCornerRadius="2dp"
    card_view:cardElevation="2dp"
    style="?attr/cardStyle">

    <TextView xmlns:android="http://schemas.android.com/apk/res/android"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:gravity="center_vertical"
        android:id="@+id/header_title"

</android.support.v7.widget.CardView>
Thusly answered 1/8, 2015 at 1:56 Comment(4)
Usefull, but what about an attribute that automatically stylish all CardViews without define the "style" for each View? Something like radioButtonStyle for radio button. I gave a look to R.Attr of CardView but it seems not present. developer.android.com/reference/android/support/v7/cardview/…Samp
Is the <declare-styleable/> really necessary or is putting <attr format="reference" name="cardStyle"/> in styles.xml under <resources/> a shorthand?Irredentist
@Samp CardView passes 0 as defStyleAttr so the only way I see is to subclass it and pass our custom attribute as a default. See my answer for details.Demonstration
@Samp trying to achieve the same thing. suspect this may not be possible because CardView is in a library outside the core Android SDKDegroot
D
2

If you don't want to add style="?cardViewStyle" to every CardView in your layouts, you can subclass CardView and set the style attribute in the constructor (too bad they didn't do that in the support library). Then use that subclass in the XML, instead of CardView.

package com.example.widget;

import android.content.Context;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.v7.widget.CardView;
import android.util.AttributeSet;
import com.example.R;

/**
 * {@link CardView} subclass that allows theme'ing through
 * the {@link R.attr#cardViewStyle} attribute.
 * <p>
 * The attribute needs to be defined, for example in <code>attrs.xml</code> as:
 * <pre>
 *   &lt;attr format="reference" name="cardViewStyle"/&gt;
 * </pre>
 * <p>
 * You'll need to set that attribute in your theme, as usual:
 * <pre>
 *   &lt;item name="cardViewStyle"&gt;@style/CardView.MyStyle&lt;/item&gt;
 * </pre>
 * And define the style itself, for example:
 * <pre>
 *   &lt;style name="CardView.MyStyle" parent="CardView.Light"&gt;
 *     &lt;item name="cardCornerRadius"&gt;0dp&lt;/item&gt;
 *   &lt;/style&gt;
 * </pre>
 */
public class StylishCardView extends CardView {
  public StylishCardView(@NonNull Context context) {
    this(context, null);
  }

  public StylishCardView(@NonNull Context context, @Nullable AttributeSet attrs) {
    this(context, attrs, R.attr.cardViewStyle);
  }

  public StylishCardView(@NonNull Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
    super(context, attrs, defStyleAttr);
  }
}

The attribute cardViewStyle needs to be defined, for example in attrs.xml as:

<attr format="reference" name="cardViewStyle"/>

You'll need to set that attribute in your theme, as usual:

<item name="cardViewStyle">@style/CardView.MyStyle</item>

And define the style itself, for example:

<style name="CardView.MyStyle" parent="CardView.Light">
    <item name="cardCornerRadius">0dp</item>
</style>
Demonstration answered 30/10, 2017 at 17:56 Comment(0)
U
0

The only solution I've been able to come up with is to manually hold a constant variable for the card color should be after every theme change. Then, I get every instance of a card that is in my application and set its color to that constant variable.

So, I have a BaseActivity from which all of my activities extend from. In it, I handle the card color change in addition to handling the theme. See below:

BaseActivity.java:

public class BaseActivity extends ActionBarActivity {
    private static final int DEFAULT_THEME_ID = R.style.AppTheme_Dark;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        int theme_id =
                PreferenceManager.getDefaultSharedPreferences(getApplicationContext()).getInt("THEME_ID", DEFAULT_THEME_ID);
        setTheme(theme_id);

        int card_color;
        if (theme_id == DEFAULT_THEME_ID){
            card_color = R.color.cv_dark;
        } else{
            card_color = R.color.cv_light;
        }
        Constants.CARD_COLOR = card_color;
        super.onCreate(savedInstanceState);
    }
}

This is definitely not the best way to do this, but until someone else can find a better way to implement styling, this is all I can think of.

Unsure answered 25/5, 2015 at 14:26 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.