将自定义的CardView样式附加到主题

24

在我的应用中,我有两个主题(浅色和深色),我希望所有的CardView根据所选主题更改它们的背景颜色。

不想要的是:

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

上面的代码不是动态的。我需要根据选择的浅色或深色主题自动应用两种样式。


目前我的代码无法正常工作:

<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>

我曾经在某个地方读到,你可以在/res/values/中定义一个styleable.xml文件来定义cardViewStyle这个词的键,所以我这样做了:

styleable.xml:

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

更新

这里有一个类似的问题,但是没有得到答案。

4个回答

21
在 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>

然后在您的另一个XML中使用新属性,您将像这样使用它

style="?attr/cardStyle"

运行得非常好!确保在您的浅色主题中添加CardView.Light,这样当您来回切换时它就会被识别。 - dabluck

10

为了更详细地展示如何实现David Park的解决方案...

将以下内容放在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>

然后,在 styles.xml 中将 cardStyle 添加到您的主题中:

<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>

然后在您有CardView的任何地方使用attr:

<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>

3
有用的建议,不过是否可以添加一个自动为所有CardViews设置样式而不需要为每个View定义"style"属性的特性呢?就像radio button的radioButtonStyle一样。我查看了CardView的R.Attr,但好像没有这个选项。http://developer.android.com/reference/android/support/v7/cardview/R.attr.html - ARLabs
<declare-styleable/> 真的必要吗?还是将 <attr format="reference" name="cardStyle"/> 放在 <resources/> 下的 styles.xml 中是一种简写方式? - binki
@ARLabs CardView将0作为defStyleAttr传递,所以我唯一能想到的方法是对其进行子类化并将我们的自定义属性作为默认值传递。有关详细信息,请参见我的答案。 - arekolek
@ARLabs 正在尝试实现相同的事情。我怀疑这可能是不可能的,因为 CardView 是在 Android 核心 SDK 之外的库中。 - Merk

2
如果您不想在布局中的每个CardView中添加style="?cardViewStyle",您可以子类化CardView并在构造函数中设置样式属性(遗憾的是支持库没有这样做)。然后在XML中使用该子类,而不是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);
  }
}

需要定义属性cardViewStyle,例如在attrs.xml中:

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

你需要像往常一样在主题中设置该属性:

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

并定义样式本身,例如:

<style name="CardView.MyStyle" parent="CardView.Light">
    <item name="cardCornerRadius">0dp</item>
</style>

0
我能想到的唯一解决方案是手动持有一个常量变量,用于在每次主题更改后设置卡片颜色应该是什么。然后,我获取我的应用程序中的每个卡片实例,并将其颜色设置为该常量变量。
因此,我有一个BaseActivity,所有我的活动都从中扩展。在其中,我处理卡片颜色变化以及处理主题。请参见以下内容:

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);
    }
}

这绝对不是最好的方法,但在其他人能找到更好的实现样式的方法之前,这是我所能想到的全部。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接