4

我有一个可以在运行时选择的具有两个主题(深色和浅色)的应用程序。这行得通。我还有一个 ListView,其中的行可以具有三种不同布局中的一种,每种布局都有一种样式(例如,不同的颜色)。这也有效。但是我不能让这两个功能一起工作。我真的需要六种不同的样式,三个用于一个主题(深色),三个用于另一个(浅色),但我不知道如何根据当前主题为列表项选择样式,或者获得任何效果通过使用 XML 文件的其他方式。我的三个布局每个都指向一个设置颜色的自定义主题,但这会覆盖我设置的任何主题。主题只能包含“可样式化”的项目,所以我不能把我自己的自定义项目放在那里。可能有办法以编程方式执行此操作,但我希望以声明方式进行。有任何想法吗?

4

2 回答 2

7

感谢僚机提示。我的情况涉及颜色,这有点复杂,所以我会在这里写下我的解决方案。

我有两个主题(浅色和深色),用户可以在“设置”屏幕中进行选择。我有一个ListView可以有两种类型的行(普通和注释),每种都有自己的样式。首先,每个布局都需要指向一个样式:

<TextView style="@style/PlainItemText" ... />

(或NoteItemText),我们需要定义样式:

<style name="PlainItemText">
    <item name="android:textSize">@dimen/list_item_font_size</item>
    <item name="android:textStyle">bold</item>
    <item name="android:textColor">?plainTextColor</item>
</style>

无法固定文本颜色,因为它取决于所选主题。我们必须创建一个自定义属性并用问号引用它,如上所述。我们在 中定义属性res/values/attrs.xml

<!-- Attributes we use to set the text color of the various list items. -->
<attr name="plainTextColor" format="reference|color"/>
<attr name="noteTextColor" format="reference|color"/>

然后我们可以定义各种颜色。这里我们有两种样式和两种主题,所以我们需要四个颜色状态列表,每个都在res/color. 例如,这里是res/color/plain_text_color_dark.xml

<selector xmlns:android="http://schemas.android.com/apk/res/android">
    <item android:state_window_focused="false" android:color="@android:color/white"/>

    <item android:state_selected="true" android:color="@android:color/black"/>
    <item android:state_focused="true" android:color="@android:color/black"/>
    <item android:state_pressed="true" android:color="@android:color/black"/>

    <item android:color="@android:color/white"/>
</selector>

所有这些文件中选择/聚焦/按下的颜色都是相同的,因为它们在突出显示颜色之上。小心state_window_focused版本。它的行为不像宣传的那样,在所有情况下我都必须将其设置为默认颜色(上面的最后一行)。现在我们需要创建主题并将属性绑定到其中一种颜色。这些行进入res/values/themes.xml

 <style name="Theme.Dark" parent="android:Theme">
     <item name="plainTextColor">@color/plain_text_color_dark</item>
     <item name="noteTextColor">@color/note_text_color_dark</item>
 </style>
 <style name="Theme.Light" parent="android:Theme.Light">
     <item name="plainTextColor">@color/plain_text_color_light</item>
     <item name="noteTextColor">@color/note_text_color_light</item>
 </style>

最后,我们在运行时在 Activity 的onCreate()方法中选择一个主题,然后调用super.onCreate()

if (isDarkTheme) {
    activity.setTheme(R.style.Theme_Dark);
} else {
    activity.setTheme(R.style.Theme_Light);
}

请注意,我没有考虑 Holo 等较新的主题,因此我的应用在 Honeycomb 及更高版本上看起来很旧。我会在某个时候解决这个问题,但这不是回归。


在我的情况下,一个转折是一些活动有一个更大的标题栏以适应一些按钮。原则上我应该创建四个主题,一个用于窄标题的明暗和一个用于胖标题的明暗。但相反,我创建了一种混合风格:

<!-- Mix-in style for activities. -->
<style name="ButtonTitleBar">
    <item name="android:windowTitleSize">44dp</item> 
</style>

并按程序将其添加到我正在使用的任何主题中。此代码在上述setTheme()调用之后立即执行:

if (buttonTitleBar) {
    // Mix in this other style.
    Resources.Theme theme = activity.getTheme();
    theme.applyStyle(R.style.ButtonTitleBar, true);
}

我没有在任何地方看到这个文档,我不知道它是否合法,但代码Activity.getTheme()暗示它应该可以正常工作,并且它在我的所有测试中都有效。这有助于避免您可以在标准 Android 主题列表中找到的主题组合爆炸式增长。

于 2012-11-07T17:24:40.500 回答
1

很久以前,Lawrence Kesteloot 在 2012 年发布了他的解决方案。现在是六年后,一个 Android 的新手,并尝试解决类似的问题:

如何通过只交换一个主题来交换应用程序的整体风格?

这是对劳伦斯问题如何组织两个可交换主题的概括。

我想出了一个基于劳伦斯的解决方案,并更进一步。

(并不是说它是完美的解决方案,而是一种改进。)

Lawrence 发现了用户定义属性的力量来实现这一目标。他根据当前选择的主题使用它们来处理颜色。

虽然这是有效的,但它仍然需要为每个属性定义属性。它不能很好地扩展。那么为什么不将属性捆绑到样式和主题中并使用相同的机制呢?

这会产生一个主主题,即定义子主题和样式。

资源/值/attrs.xml

<resources>
     ...
    <attr name="mainViewTheme" format="string"/>
    <attr name="asideViewTheme" format="string"/>
     ...
</resources>

在定义属性来设置主题时,它没有特殊的格式。格式字符串可以做到这一点。

res/values/styles.xml

<style name="MasterTheme">
     ...
    <item name="mainViewTheme">@style/MainViewTheme</item>
    <item name="asideViewTheme">@style/AsideViewTheme</item>
     ...
</style>

<style name="MainTextTheme">
     ...
</style>

<style name="MainViewTheme">
     ...
</style>

资源/布局/main.xml

<TextView
    android:theme="?mainViewTheme"
    ...

通过交换主主题,所有风格都得到了调整。它仍然需要定义一些主题属性,然后完成一项强大的工作。不再需要为每个属性设置属性。

于 2018-02-10T19:52:13.807 回答