将 AppCompat 主题应用于 PreferenceFragment 中的个人首选项
Applying AppCompat theme to individual preferences in a PreferenceFragment
我一直在努力让我的 PreferenceFragment
与我的应用程序的其余部分具有相同的基于 Material 的主题和样式(通过 AppCompat)。我用来管理所有应用程序设置的 PreferenceFragment
如下所示:
正如您从上面的屏幕截图中看到的,我能够通过使用 colorAccent
、colorPrimary
和一些其他属性来自定义 PreferenceFragment
。我的PreferenceFragment
主题如下:
<style
name="settingsTheme"
parent="Theme.AppCompat.Light.DarkActionBar">
<item name="colorPrimary">@color/blue_grey_500</item>
<item name="colorAccent">@color/blue_grey_500</item>
<item name="android:textColor">@color/black_text_alt</item>
<item name="android:textColorSecondary">@color/black_secondary_text</item>
</style>
然而,尽管我尽了最大努力,但我无法将主题应用到我的 PreferenceFragment
中的各个元素,例如 ListPreference
和 EditTextPreference
;所有首选项仍保留标准 AppCompat 主题:
我发现一个 2 岁的 post 讨论了这个问题,尽管没有真正的解决方案:How to apply theme to <PreferenceScreen> elements of a <PreferenceCategory>
我想知道自 AppComat V21 发布以来是否有人能够成功地将主题应用到首选项中。如果没有,是否有任何可行的解决方法可用于将自定义主题应用于个人偏好?
更新: 我制作了一个库来解决这个问题(使用 AppCompat r22):https://github.com/consp1racy/android-support-preference
ListPreference
扩展了 DialogPreference
,它使用这段代码来创建对话框:
mBuilder = new AlertDialog.Builder(context)
.setTitle(mDialogTitle)
.setIcon(mDialogIcon)
.setPositiveButton(mPositiveButtonText, this)
.setNegativeButton(mNegativeButtonText, this);
如您所见,AlertDialog.Builder
构造函数未随第二个可选 int theme
参数一起提供。这意味着对话框将以您 activity 的主题在其 android:alertDialogTheme
属性中的任何内容为主题。
现在您必须为源自 Theme.AppCompat.Dialog
的对话框创建自定义主题,如下所示:
<style name="Theme.YourApp.Dialog.Alert" parent="Theme.AppCompat.Light.Dialog">
<item name="android:windowBackground">@android:color/transparent</item>
<item name="android:windowContentOverlay">@null</item>
<item name="android:windowMinWidthMajor">@android:dimen/dialog_min_width_major</item>
<item name="android:windowMinWidthMinor">@android:dimen/dialog_min_width_minor</item>
<item name="colorAccent">@color/accent_yourapp</item>
<item name="colorPrimary">@color/primary_yourapp</item>
<item name="colorPrimaryDark">@color/primary_dark_yourapp</item>
</style>
问题1:上面的解决方案对RingtonePreference
不起作用,因为它不扩展ListPreference
而是调用铃声选择器意图,所以它总是根据系统主题。看看这个答案:
RingtonePreference Theme 所以我们可以将其标记为已解决。
问题 2: AppCompat 对话框缺少标题。到目前为止,我还没有找到解决这个问题的方法。没错,我看起来不够努力。让我们忽略标题缺失作为一个小问题。
问题 3: 单选按钮可绘制对象未发生变化,因此图形在被动和主动(彩色)状态之间不一致 - 所有颜色都是彩色的(不仅仅是你!重新)按)或全部为灰色。现在这真的很烦人
问题 2 和 3 迫使我走另一条路 - 我的对话主题在 API 14+
上看起来像这样
<style name="Theme.MyApp.Dialog.Alert" parent="android:Theme.DeviceDefault.Light.Dialog.MinWidth">
<item name="android:windowBackground">@android:color/transparent</item>
<item name="android:windowContentOverlay">@null</item>
</style>
并在 API 21+
点赞
<style name="Theme.YourApp.Dialog.Alert" parent="android:Theme.DeviceDefault.Light.Dialog.MinWidth">
<item name="android:colorPrimary">@color/primary_yourapp</item>
<item name="android:colorPrimaryDark">@color/primary_dark_yourapp</item>
<item name="android:colorAccent">@color/accent_yourapp</item>
</style>
这些值是通过爬取平台的源文件并经过充分测试以实验方式获得的。
关键是唯一可靠的解决方案似乎是使用设备默认对话框主题。 Lollipop 之前的唯一选择是浅色或深色变体。在 Lollipop 上,这将按预期和要求工作。
编辑: 自 appcompat-v7-r21.1.0 以来,您可以使用 AppCompatDialog
,它是原生 AlertDialog
的 material 主题变体。
您可以使用提供的 AlertDialog.Builder
(不要与其原生对应物混淆)来创建其实例。
您应该查看 this Gist 了解不同的组件如何使用主题颜色属性。您可能需要将以下属性添加到您的主题中以获得您想要的效果:
<item name="colorControlNormal">@color/x_color</item>
<item name="colorControlActivated">@color/y_color</item>
<item name="colorControlHighlight">@color/z_color</item>
DialogPreference
直接使用 android.app.AlertDialog.Builder
因此不可能将显示的对话框切换到 material 设计的所有 API 级别(这需要使用 android.support.v7.widget.AlertDialog.Builder
).
我认为目前唯一的选择是扩展和覆盖 DialogPreference
或其子类中的对话框创建逻辑(并在需要时使用反射来解决私有访问器)。查看该线程作者制作的reddit thread and an example AppCompatListPreference
。
根据已经给出的答案,我在清单中为设置 activity 设置了一个自定义主题,因为有必要删除出现的对话框周围的边框背景,使用透明背景这仅适用于 API <21:
AndroidManifest.xml
<application
android:name=".MyApplication"
...
android:theme="@style/AppTheme">
...
<activity
android:name=".ui.SettingsActivity"
...
android:theme="@style/AppTheme.Settings">
</activity>
</application>
values/style.xml
<!-- Application theme -->
<style name="AppTheme" parent="AppTheme.Base">
<!-- All customizations that are NOT specific to a particular API-level can go here. -->
</style>
<style name="AppTheme.Base" parent="Theme.AppCompat.Light.NoActionBar">
...
<item name="android:alertDialogTheme">@style/AppTheme.AlertDialog</item>
</style>
<!-- Must be different because of transparent background -->
<style name="AppTheme.Settings" parent="AppTheme.Base">
<item name="android:alertDialogTheme">@style/AppTheme.SettingsAlertDialog</item>
</style>
<style name="AppTheme.AlertDialog" parent="Theme.AppCompat.Light.Dialog.Alert">
<item name="android:windowMinWidthMajor">@android:dimen/dialog_min_width_major</item>
<item name="android:windowMinWidthMinor">@android:dimen/dialog_min_width_minor</item>
<item name="android:windowContentOverlay">@null</item>
<item name="colorAccent">@color/accent_light</item>
<item name="colorControlActivated">@color/accent_light</item>
<item name="colorControlHighlight">@color/primary_highlight</item>
</style>
<style name="AppTheme.SettingsAlertDialog" parent="Theme.AppCompat.Light.Dialog.Alert">
<item name="android:windowMinWidthMajor">@android:dimen/dialog_min_width_major</item>
<item name="android:windowMinWidthMinor">@android:dimen/dialog_min_width_minor</item>
<item name="android:windowBackground">@android:color/transparent</item>
<item name="android:windowContentOverlay">@null</item>
<item name="colorAccent">@color/accent_light</item>
<item name="colorControlActivated">@color/accent_light</item>
<item name="colorControlHighlight">@color/primary_highlight</item>
</style>
最后 values-v21/style.xml 普通对话框,如果你不把它放在 v21 对话框中将是透明的。
<style name="AppTheme.Settings" parent="AppTheme.Base">
<item name="android:alertDialogTheme">@style/AppTheme.AlertDialog</item>
</style>
实际上我无法直接从主题属性设置颜色:?android:colorAccent 而不是我的固定颜色@color/accent_light.
我一直在努力让我的 PreferenceFragment
与我的应用程序的其余部分具有相同的基于 Material 的主题和样式(通过 AppCompat)。我用来管理所有应用程序设置的 PreferenceFragment
如下所示:
正如您从上面的屏幕截图中看到的,我能够通过使用 colorAccent
、colorPrimary
和一些其他属性来自定义 PreferenceFragment
。我的PreferenceFragment
主题如下:
<style
name="settingsTheme"
parent="Theme.AppCompat.Light.DarkActionBar">
<item name="colorPrimary">@color/blue_grey_500</item>
<item name="colorAccent">@color/blue_grey_500</item>
<item name="android:textColor">@color/black_text_alt</item>
<item name="android:textColorSecondary">@color/black_secondary_text</item>
</style>
然而,尽管我尽了最大努力,但我无法将主题应用到我的 PreferenceFragment
中的各个元素,例如 ListPreference
和 EditTextPreference
;所有首选项仍保留标准 AppCompat 主题:
我发现一个 2 岁的 post 讨论了这个问题,尽管没有真正的解决方案:How to apply theme to <PreferenceScreen> elements of a <PreferenceCategory>
我想知道自 AppComat V21 发布以来是否有人能够成功地将主题应用到首选项中。如果没有,是否有任何可行的解决方法可用于将自定义主题应用于个人偏好?
更新: 我制作了一个库来解决这个问题(使用 AppCompat r22):https://github.com/consp1racy/android-support-preference
ListPreference
扩展了 DialogPreference
,它使用这段代码来创建对话框:
mBuilder = new AlertDialog.Builder(context)
.setTitle(mDialogTitle)
.setIcon(mDialogIcon)
.setPositiveButton(mPositiveButtonText, this)
.setNegativeButton(mNegativeButtonText, this);
如您所见,AlertDialog.Builder
构造函数未随第二个可选 int theme
参数一起提供。这意味着对话框将以您 activity 的主题在其 android:alertDialogTheme
属性中的任何内容为主题。
现在您必须为源自 Theme.AppCompat.Dialog
的对话框创建自定义主题,如下所示:
<style name="Theme.YourApp.Dialog.Alert" parent="Theme.AppCompat.Light.Dialog">
<item name="android:windowBackground">@android:color/transparent</item>
<item name="android:windowContentOverlay">@null</item>
<item name="android:windowMinWidthMajor">@android:dimen/dialog_min_width_major</item>
<item name="android:windowMinWidthMinor">@android:dimen/dialog_min_width_minor</item>
<item name="colorAccent">@color/accent_yourapp</item>
<item name="colorPrimary">@color/primary_yourapp</item>
<item name="colorPrimaryDark">@color/primary_dark_yourapp</item>
</style>
问题1:上面的解决方案对RingtonePreference
不起作用,因为它不扩展ListPreference
而是调用铃声选择器意图,所以它总是根据系统主题。看看这个答案:
RingtonePreference Theme 所以我们可以将其标记为已解决。
问题 2: AppCompat 对话框缺少标题。到目前为止,我还没有找到解决这个问题的方法。没错,我看起来不够努力。让我们忽略标题缺失作为一个小问题。
问题 3: 单选按钮可绘制对象未发生变化,因此图形在被动和主动(彩色)状态之间不一致 - 所有颜色都是彩色的(不仅仅是你!重新)按)或全部为灰色。现在这真的很烦人
问题 2 和 3 迫使我走另一条路 - 我的对话主题在 API 14+
上看起来像这样<style name="Theme.MyApp.Dialog.Alert" parent="android:Theme.DeviceDefault.Light.Dialog.MinWidth">
<item name="android:windowBackground">@android:color/transparent</item>
<item name="android:windowContentOverlay">@null</item>
</style>
并在 API 21+
点赞<style name="Theme.YourApp.Dialog.Alert" parent="android:Theme.DeviceDefault.Light.Dialog.MinWidth">
<item name="android:colorPrimary">@color/primary_yourapp</item>
<item name="android:colorPrimaryDark">@color/primary_dark_yourapp</item>
<item name="android:colorAccent">@color/accent_yourapp</item>
</style>
这些值是通过爬取平台的源文件并经过充分测试以实验方式获得的。
关键是唯一可靠的解决方案似乎是使用设备默认对话框主题。 Lollipop 之前的唯一选择是浅色或深色变体。在 Lollipop 上,这将按预期和要求工作。
编辑: 自 appcompat-v7-r21.1.0 以来,您可以使用 AppCompatDialog
,它是原生 AlertDialog
的 material 主题变体。
您可以使用提供的 AlertDialog.Builder
(不要与其原生对应物混淆)来创建其实例。
您应该查看 this Gist 了解不同的组件如何使用主题颜色属性。您可能需要将以下属性添加到您的主题中以获得您想要的效果:
<item name="colorControlNormal">@color/x_color</item>
<item name="colorControlActivated">@color/y_color</item>
<item name="colorControlHighlight">@color/z_color</item>
DialogPreference
直接使用 android.app.AlertDialog.Builder
因此不可能将显示的对话框切换到 material 设计的所有 API 级别(这需要使用 android.support.v7.widget.AlertDialog.Builder
).
我认为目前唯一的选择是扩展和覆盖 DialogPreference
或其子类中的对话框创建逻辑(并在需要时使用反射来解决私有访问器)。查看该线程作者制作的reddit thread and an example AppCompatListPreference
。
根据已经给出的答案,我在清单中为设置 activity 设置了一个自定义主题,因为有必要删除出现的对话框周围的边框背景,使用透明背景这仅适用于 API <21:
AndroidManifest.xml
<application
android:name=".MyApplication"
...
android:theme="@style/AppTheme">
...
<activity
android:name=".ui.SettingsActivity"
...
android:theme="@style/AppTheme.Settings">
</activity>
</application>
values/style.xml
<!-- Application theme -->
<style name="AppTheme" parent="AppTheme.Base">
<!-- All customizations that are NOT specific to a particular API-level can go here. -->
</style>
<style name="AppTheme.Base" parent="Theme.AppCompat.Light.NoActionBar">
...
<item name="android:alertDialogTheme">@style/AppTheme.AlertDialog</item>
</style>
<!-- Must be different because of transparent background -->
<style name="AppTheme.Settings" parent="AppTheme.Base">
<item name="android:alertDialogTheme">@style/AppTheme.SettingsAlertDialog</item>
</style>
<style name="AppTheme.AlertDialog" parent="Theme.AppCompat.Light.Dialog.Alert">
<item name="android:windowMinWidthMajor">@android:dimen/dialog_min_width_major</item>
<item name="android:windowMinWidthMinor">@android:dimen/dialog_min_width_minor</item>
<item name="android:windowContentOverlay">@null</item>
<item name="colorAccent">@color/accent_light</item>
<item name="colorControlActivated">@color/accent_light</item>
<item name="colorControlHighlight">@color/primary_highlight</item>
</style>
<style name="AppTheme.SettingsAlertDialog" parent="Theme.AppCompat.Light.Dialog.Alert">
<item name="android:windowMinWidthMajor">@android:dimen/dialog_min_width_major</item>
<item name="android:windowMinWidthMinor">@android:dimen/dialog_min_width_minor</item>
<item name="android:windowBackground">@android:color/transparent</item>
<item name="android:windowContentOverlay">@null</item>
<item name="colorAccent">@color/accent_light</item>
<item name="colorControlActivated">@color/accent_light</item>
<item name="colorControlHighlight">@color/primary_highlight</item>
</style>
最后 values-v21/style.xml 普通对话框,如果你不把它放在 v21 对话框中将是透明的。
<style name="AppTheme.Settings" parent="AppTheme.Base">
<item name="android:alertDialogTheme">@style/AppTheme.AlertDialog</item>
</style>
实际上我无法直接从主题属性设置颜色:?android:colorAccent 而不是我的固定颜色@color/accent_light.