从应用程序主题继承 AppCompat 22.1.1 对话框 colorAccent 不起作用
Inheriting AppCompat 22.1.1 Dialog colorAccent from app theme doesn't work
我正在尝试设置 AppCompat 的对话框,以便按钮使用与应用程序的强调色相同的颜色,而不重复颜色本身。通过在 values-v21
:
中使用此样式文件,这与 AppCompat v22(显然仅适用于 Lollipop)完美配合
<style name="AppTheme" parent="@style/Theme.AppCompat">
<item name="colorAccent">#FF9800</item>
<item name="android:alertDialogTheme">@style/AlertDialogTheme</item>
</style>
<style name="AlertDialogTheme" parent="android:Theme.Material.Dialog.Alert">
<item name="android:colorAccent">?attr/colorAccent</item>
</style>
当 AppCompat v22.1 was released 我试图为所有 Android 版本设置它,所以我将这些样式移动到基础 values
文件夹,并且基本上替换了所有 android:
和 v21 特定的属性由它们的 AppCompat 对应项提供。
<style name="AppTheme" parent="Theme.AppCompat">
<item name="colorAccent">#FF9800</item>
<item name="alertDialogTheme">@style/AlertDialogTheme</item>
</style>
<style name="AlertDialogTheme" parent="Theme.AppCompat.Dialog.Alert">
<item name="colorAccent">?attr/colorAccent</item>
</style>
但是,它不起作用 -- 应用程序 在尝试显示警告对话框时崩溃 。 logcat 中有一些警告,我强烈怀疑这些警告与问题有关:
05-08 16:55:44.863 W/ResourceType﹕ Too many attribute references, stopped at: 0x7f01009e
例外情况是:
05-08 16:55:44.900 21301-21301/com.example.test.testaccentcolor E/AndroidRuntime﹕ FATAL EXCEPTION: main
Process: com.example.test.testaccentcolor, PID: 21301
android.view.InflateException: Binary XML file line #124: Error inflating class Button
at android.view.LayoutInflater.createViewFromTag(LayoutInflater.java:763)
at android.view.LayoutInflater.rInflate(LayoutInflater.java:806)
at android.view.LayoutInflater.rInflate(LayoutInflater.java:809)
at android.view.LayoutInflater.inflate(LayoutInflater.java:504)
at android.view.LayoutInflater.inflate(LayoutInflater.java:414)
at android.view.LayoutInflater.inflate(LayoutInflater.java:365)
at android.support.v7.app.AppCompatDelegateImplV7.setContentView(AppCompatDelegateImplV7.java:249)
at android.support.v7.app.AppCompatDialog.setContentView(AppCompatDialog.java:75)
at android.support.v7.app.AlertController.installContent(AlertController.java:216)
at android.support.v7.app.AlertDialog.onCreate(AlertDialog.java:240)
at android.app.Dialog.dispatchOnCreate(Dialog.java:373)
at android.app.Dialog.show(Dialog.java:274)
at android.support.v7.app.AlertDialog$Builder.show(AlertDialog.java:902)
at com.example.test.testaccentcolor.MainActivity.onOptionsItemSelected(MainActivity.java:37)
at android.app.Activity.onMenuItemSelected(Activity.java:2885)
at android.support.v4.app.FragmentActivity.onMenuItemSelected(FragmentActivity.java:353)
at android.support.v7.app.AppCompatActivity.onMenuItemSelected(AppCompatActivity.java:144)
at android.support.v7.internal.view.WindowCallbackWrapper.onMenuItemSelected(WindowCallbackWrapper.java:99)
at android.support.v7.app.AppCompatDelegateImplV7.onMenuItemSelected(AppCompatDelegateImplV7.java:538)
at android.support.v7.internal.view.menu.MenuBuilder.dispatchMenuItemSelected(MenuBuilder.java:802)
at android.support.v7.internal.view.menu.MenuItemImpl.invoke(MenuItemImpl.java:153)
at android.support.v7.internal.view.menu.MenuBuilder.performItemAction(MenuBuilder.java:949)
at android.support.v7.internal.view.menu.MenuBuilder.performItemAction(MenuBuilder.java:939)
at android.support.v7.internal.view.menu.MenuPopupHelper.onItemClick(MenuPopupHelper.java:187)
at android.widget.AdapterView.performItemClick(AdapterView.java:305)
at android.widget.AbsListView.performItemClick(AbsListView.java:1146)
at android.widget.AbsListView$PerformClick.run(AbsListView.java:3053)
at android.widget.AbsListView.run(AbsListView.java:3860)
at android.os.Handler.handleCallback(Handler.java:739)
at android.os.Handler.dispatchMessage(Handler.java:95)
at android.os.Looper.loop(Looper.java:135)
at android.app.ActivityThread.main(ActivityThread.java:5254)
at java.lang.reflect.Method.invoke(Native Method)
at java.lang.reflect.Method.invoke(Method.java:372)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:903)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:698)
Caused by: java.lang.RuntimeException: Failed to resolve attribute at index 5
at android.content.res.TypedArray.getColorStateList(TypedArray.java:425)
at android.widget.TextView.<init>(TextView.java:991)
at android.widget.Button.<init>(Button.java:111)
at android.widget.Button.<init>(Button.java:107)
at android.support.v7.widget.AppCompatButton.<init>(AppCompatButton.java:60)
at android.support.v7.widget.AppCompatButton.<init>(AppCompatButton.java:56)
at android.support.v7.internal.app.AppCompatViewInflater.createView(AppCompatViewInflater.java:97)
at android.support.v7.app.AppCompatDelegateImplV7.createView(AppCompatDelegateImplV7.java:782)
at android.support.v7.app.AppCompatDelegateImplV7.onCreateView(AppCompatDelegateImplV7.java:810)
at android.support.v4.view.LayoutInflaterCompatHC$FactoryWrapperHC.onCreateView(LayoutInflaterCompatHC.java:44)
at android.view.LayoutInflater.createViewFromTag(LayoutInflater.java:725)
at android.view.LayoutInflater.rInflate(LayoutInflater.java:806)
at android.view.LayoutInflater.rInflate(LayoutInflater.java:809)
at android.view.LayoutInflater.inflate(LayoutInflater.java:504)
at android.view.LayoutInflater.inflate(LayoutInflater.java:414)
at android.view.LayoutInflater.inflate(LayoutInflater.java:365)
at android.support.v7.app.AppCompatDelegateImplV7.setContentView(AppCompatDelegateImplV7.java:249)
at android.support.v7.app.AppCompatDialog.setContentView(AppCompatDialog.java:75)
at android.support.v7.app.AlertController.installContent(AlertController.java:216)
at android.support.v7.app.AlertDialog.onCreate(AlertDialog.java:240)
at android.app.Dialog.dispatchOnCreate(Dialog.java:373)
at android.app.Dialog.show(Dialog.java:274)
at android.support.v7.app.AlertDialog$Builder.show(AlertDialog.java:902)
at com.example.test.testaccentcolor.MainActivity.onOptionsItemSelected(MainActivity.java:37)
at android.app.Activity.onMenuItemSelected(Activity.java:2885)
at android.support.v4.app.FragmentActivity.onMenuItemSelected(FragmentActivity.java:353)
at android.support.v7.app.AppCompatActivity.onMenuItemSelected(AppCompatActivity.java:144)
at android.support.v7.internal.view.WindowCallbackWrapper.onMenuItemSelected(WindowCallbackWrapper.java:99)
at android.support.v7.app.AppCompatDelegateImplV7.onMenuItemSelected(AppCompatDelegateImplV7.java:538)
at android.support.v7.internal.view.menu.MenuBuilder.dispatchMenuItemSelected(MenuBuilder.java:802)
at android.support.v7.internal.view.menu.MenuItemImpl.invoke(MenuItemImpl.java:153)
at android.support.v7.internal.view.menu.MenuBuilder.performItemAction(MenuBuilder.java:949)
at android.support.v7.internal.view.menu.MenuBuilder.performItemAction(MenuBuilder.java:939)
at android.support.v7.internal.view.menu.MenuPopupHelper.onItemClick(MenuPopupHelper.java:187)
at android.widget.AdapterView.performItemClick(AdapterView.java:305)
at android.widget.AbsListView.performItemClick(AbsListView.java:1146)
at android.widget.AbsListView$PerformClick.run(AbsListView.java:3053)
at android.widget.AbsListView.run(AbsListView.java:3860)
at android.os.Handler.handleCallback(Handler.java:739)
at android.os.Handler.dispatchMessage(Handler.java:95)
at android.os.Looper.loop(Looper.java:135)
at android.app.ActivityThread.main(ActivityThread.java:5254)
at java.lang.reflect.Method.invoke(Native Method)
at java.lang.reflect.Method.invoke(Method.java:372)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:903)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:698)
当然,复制颜色值(或创建颜色资源并引用它)是可行的...但是由于项目结构,我需要这种方式。
总结一下:我假设对 ?attr/colorAccent
的引用以某种方式创建了一个循环...但我不知道为什么或如何解决它。
有什么想法吗?
更新:为什么我need/want只在主题中设置accentColor
?
我们有一个许多应用程序通用的库,它定义了 "base" 必须派生实际应用程序主题的主题。所以上面的例子,经过简化,实际上更类似于这样的东西:
<!-- Styles that are inherited by application-generated styles. -->
<style name="Theme.Library.Dark" parent="@style/Theme.AppCompat">
<item name="alertDialogTheme">@style/Theme.Library.Dark.AlertDialog</item>
<!-- more properties -->
</style>
<style name="Theme.Library.Dark.AlertDialog" parent="@style/Theme.AppCompat.Dialog.Alert">
<item name="colorAccent">?attr/colorAccent</item>
<!-- more properties -->
</style>
(加上 Light 和 Light with Dark Action Bar 的相应变体)。
在此之后,应用程序项目应该只从提供的列表中选择一个基本主题,然后有类似的东西:
<!-- Application theme -->
<style name="ApplicationTheme" parent="Theme.Library.Dark">
<-- Ohter material colors -->
<item name="colorAccent">#FF9800</item>
</style>
至少这是理想的情况,以前有效(正如@Vikram 在他的回答中指出的那样,显然只是因为有两个不同的 colorAccent
属性)。
这个方案的优点是我不会被迫在库主题中为 colorAccent
提供默认值,因为它们是从 AppCompat 基类继承的。
此外,如果我需要创建颜色资源,我需要其中两个(用于浅色和深色变体),因为默认颜色不同。
因此,理想情况下,我正在寻找类似的解决方案(前提是存在)。
我相信 ?attr/colorAccent
这里指的是您要设置的那个:
<item name="colorAccent">?attr/colorAccent</item>
您可以将强调色提取到 @color
属性,然后将其用于应用程序主题和对话框主题。
colors.xml
<color name="accent">#FF9800</color>
styles.xml
<style name="AppTheme" parent="Theme.AppCompat">
<item name="colorAccent">@color/accent</item>
<item name="alertDialogTheme">@style/AlertDialogTheme</item>
</style>
<style name="AlertDialogTheme" parent="Theme.AppCompat.Dialog.Alert">
<item name="colorAccent">@color/accent</item>
</style>
<item name="colorAccent">?attr/colorAccent</item>
您已经知道这是行不通的。原因如下:
ssize_t ResTable::Theme::getAttribute(uint32_t resID, Res_value* outValue,
uint32_t* outTypeSpecFlags) const
{
int cnt = 20;
if (outTypeSpecFlags != NULL) *outTypeSpecFlags = 0;
do {
....
....
if (type == Res_value::TYPE_ATTRIBUTE) {
if (cnt > 0) {
cnt--;
resID = te.value.data;
continue;
}
ALOGW("Too many attribute references, stopped at: 0x%08x\n", resID);
return BAD_INDEX;
}
....
....
} while (true);
return BAD_INDEX;
}
该片段不言自明。你可以拥有:
<item name="colorZ" >?attr/colorY</item>
然后:
<item name="colorY" >?attr/colorX</item>
.....
....
但是像这样的链接属性不能超过 20 层深。 Android 因为 Too many attribute references...
放弃了。
在您的例子中,您是通过尝试读取属性的当前值来设置属性的值。 Android 去寻找,却找到了另一个参考——无尽的追求。
为什么之前可以用?
<style name="AppTheme" parent="@style/Theme.AppCompat">
<item name="colorAccent">#FF9800</item>
<item name="android:alertDialogTheme">@style/AlertDialogTheme</item>
</style>
<style name="AlertDialogTheme" parent="android:Theme.Material.Dialog.Alert">
<item name="android:colorAccent">?attr/colorAccent</item>
</style>
在 AppTheme
中,您正在设置支持库的 colorAccent
属性(没有 android: 命名空间)。在 AlertDialogTheme
下,您将 android:colorAccent
设置为 支持库的 colorAccent
。这就是无限循环没有形成的原因。
?attr/colorAccent
在创建 AlertDialog
时传递的 Context
中解析。为此,针对<item name="colorAccent">#FF9800</item>
.
进行了解析
一种可能的解决方法是在 res/values/attrs.xml
:
中定义自定义 attr
<attr name="alertDialogColorAccent" format="reference|color"/>
现在,我们可以在AppTheme
下初始化这个属性:
<style name="AppTheme" parent="@style/Theme.AppCompat">
<item name="colorAccent">#FF9800</item>
<item name="alertDialogColorAccent" >?attr/colorAccent</item>
<item name="android:alertDialogTheme">@style/AlertDialogTheme</item>
</style>
并在AlertDialogTheme
中使用它:
<style name="AlertDialogTheme" parent="android:Theme.Material.Dialog.Alert">
<item name="colorAccent">?attr/alertDialogColorAccent</item>
</style>
但是,这仍然行不通。它仍然会创建一个循环 - colorAccent
是 alertDialogColorAccent
& alertDialogColorAccent
是 colorAccent
。在链的某处,需要设置实际颜色值:
<style name="AppTheme" parent="@style/Theme.AppCompat">
<item name="colorAccent">#FF9800</item>
<item name="alertDialogColorAccent" >#FF9800</item>
<item name="android:alertDialogTheme">@style/AlertDialogTheme</item>
</style>
现在,您可以用 alertDialogColorAccent
初始化 colorAccent
,因为它指的是实际颜色值:
<style name="AlertDialogTheme" parent="android:Theme.Material.Dialog.Alert">
<item name="colorAccent">?attr/alertDialogColorAccent</item>
</style>
基于@Vikram 的出色回答,我最终得到了这个(我把它放在一起以供将来参考,以防有人对实际解决方案感兴趣)。
对于基本样式文件,在 values
中(注意:不同的 XML 属性、颜色和 c 文件是更可取的——为了紧凑,它们都放在一起):
<?xml version="1.0" encoding="utf-8"?>
<resources>
<style name="BaseAppTheme" parent="Theme.AppCompat.Light.DarkActionBar" />
<attr name="dialogColorAccent" />
<color name="myAccentColor">#FF9800</color>
<style name="AppTheme" parent="BaseAppTheme">
<item name="dialogTheme">@style/DialogTheme</item>
<item name="alertDialogTheme">@style/AlertDialogTheme</item>
<item name="colorAccent">@color/myAccentColor</item>
<item name="dialogColorAccent">@color/myAccentColor</item>
</style>
<style name="DialogTheme" parent="Theme.AppCompat.Light.Dialog">
<item name="colorAccent">?attr/dialogColorAccent</item>
</style>
<style name="AlertDialogTheme" parent="Theme.AppCompat.Light.Dialog.Alert">
<item name="colorAccent">?attr/dialogColorAccent</item>
</style>
</resources>
新的 dialogColorAccent
属性是必需的,以便可以从对话框样式中引用它。不幸的是,它不能引用 colorAccent
本身(反过来也不可能)所以两者必须具有相同的值(在这种情况下,是对颜色资源的引用)。
这可确保使用 android.support.v7.app.AlertDialog.Builder
构建的 AlertDialogs 对所有 Android 版本的 positive/negative 按钮使用强调色。
但是,某些对话框(例如 DatePickerDialog
、TimePickerDialog
和 ProgressDialog
)在 Lollipop 中具有 material 版本但没有 AppCompat 等效项,因此需要额外的文件迄今为止。因此,以下样式文件进入 values-v21
:
<?xml version="1.0" encoding="utf-8"?>
<resources>
<style name="BaseAppTheme" parent="Theme.AppCompat.Light.DarkActionBar">
<item name="android:dialogTheme">@style/DialogTheme</item>
</style>
</resources>
这将使这些新对话框也具有强调色(但显然仅限于 Lollipop)。
解决方案可能看起来有点复杂(从 AlertDialogTheme
样式引用 @myAccentColor
更简单 :))但这是必要的,因为库中定义了多个主题,而且这个想法是这些主题可以被继承并以最小的变化使用。
我在对话框主题中将 android:textColor
设置为 @null
,这很有帮助。
styles.xml
<style name="AlertDialogTheme" parent="@style/Theme.AppCompat.Light.Dialog.Alert">
<!-- Used for the buttons -->
<item name="colorAccent">@color/colorAccent</item>
<item name="android:textColor">@null</item>
</style>
此外,这个 trick
帮助我解决了其他按钮的 android:textColor
问题。
<style name="Button.Base.Borderless" parent="Widget.AppCompat.Button.Borderless.Colored">
<item name="android:textColor">@null</item>
</style>
some_layout.xml
<Button
style="@style/Button.Base.Borderless"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/hint" />
现在按钮文本颜色是 colorAccent
在 AppTheme
中定义
<style name="AppTheme" parent="@style/Theme.AppCompat.Light.NoActionBar">
<item name="colorAccent">@color/colorAccent</item>
<item name="borderlessButtonStyle">@style/Button.Base.Borderless</item>
<item name="alertDialogTheme">@style/AlertDialog</item>
</style>
我正在尝试设置 AppCompat 的对话框,以便按钮使用与应用程序的强调色相同的颜色,而不重复颜色本身。通过在 values-v21
:
<style name="AppTheme" parent="@style/Theme.AppCompat">
<item name="colorAccent">#FF9800</item>
<item name="android:alertDialogTheme">@style/AlertDialogTheme</item>
</style>
<style name="AlertDialogTheme" parent="android:Theme.Material.Dialog.Alert">
<item name="android:colorAccent">?attr/colorAccent</item>
</style>
当 AppCompat v22.1 was released 我试图为所有 Android 版本设置它,所以我将这些样式移动到基础 values
文件夹,并且基本上替换了所有 android:
和 v21 特定的属性由它们的 AppCompat 对应项提供。
<style name="AppTheme" parent="Theme.AppCompat">
<item name="colorAccent">#FF9800</item>
<item name="alertDialogTheme">@style/AlertDialogTheme</item>
</style>
<style name="AlertDialogTheme" parent="Theme.AppCompat.Dialog.Alert">
<item name="colorAccent">?attr/colorAccent</item>
</style>
但是,它不起作用 -- 应用程序 在尝试显示警告对话框时崩溃 。 logcat 中有一些警告,我强烈怀疑这些警告与问题有关:
05-08 16:55:44.863 W/ResourceType﹕ Too many attribute references, stopped at: 0x7f01009e
例外情况是:
05-08 16:55:44.900 21301-21301/com.example.test.testaccentcolor E/AndroidRuntime﹕ FATAL EXCEPTION: main
Process: com.example.test.testaccentcolor, PID: 21301
android.view.InflateException: Binary XML file line #124: Error inflating class Button
at android.view.LayoutInflater.createViewFromTag(LayoutInflater.java:763)
at android.view.LayoutInflater.rInflate(LayoutInflater.java:806)
at android.view.LayoutInflater.rInflate(LayoutInflater.java:809)
at android.view.LayoutInflater.inflate(LayoutInflater.java:504)
at android.view.LayoutInflater.inflate(LayoutInflater.java:414)
at android.view.LayoutInflater.inflate(LayoutInflater.java:365)
at android.support.v7.app.AppCompatDelegateImplV7.setContentView(AppCompatDelegateImplV7.java:249)
at android.support.v7.app.AppCompatDialog.setContentView(AppCompatDialog.java:75)
at android.support.v7.app.AlertController.installContent(AlertController.java:216)
at android.support.v7.app.AlertDialog.onCreate(AlertDialog.java:240)
at android.app.Dialog.dispatchOnCreate(Dialog.java:373)
at android.app.Dialog.show(Dialog.java:274)
at android.support.v7.app.AlertDialog$Builder.show(AlertDialog.java:902)
at com.example.test.testaccentcolor.MainActivity.onOptionsItemSelected(MainActivity.java:37)
at android.app.Activity.onMenuItemSelected(Activity.java:2885)
at android.support.v4.app.FragmentActivity.onMenuItemSelected(FragmentActivity.java:353)
at android.support.v7.app.AppCompatActivity.onMenuItemSelected(AppCompatActivity.java:144)
at android.support.v7.internal.view.WindowCallbackWrapper.onMenuItemSelected(WindowCallbackWrapper.java:99)
at android.support.v7.app.AppCompatDelegateImplV7.onMenuItemSelected(AppCompatDelegateImplV7.java:538)
at android.support.v7.internal.view.menu.MenuBuilder.dispatchMenuItemSelected(MenuBuilder.java:802)
at android.support.v7.internal.view.menu.MenuItemImpl.invoke(MenuItemImpl.java:153)
at android.support.v7.internal.view.menu.MenuBuilder.performItemAction(MenuBuilder.java:949)
at android.support.v7.internal.view.menu.MenuBuilder.performItemAction(MenuBuilder.java:939)
at android.support.v7.internal.view.menu.MenuPopupHelper.onItemClick(MenuPopupHelper.java:187)
at android.widget.AdapterView.performItemClick(AdapterView.java:305)
at android.widget.AbsListView.performItemClick(AbsListView.java:1146)
at android.widget.AbsListView$PerformClick.run(AbsListView.java:3053)
at android.widget.AbsListView.run(AbsListView.java:3860)
at android.os.Handler.handleCallback(Handler.java:739)
at android.os.Handler.dispatchMessage(Handler.java:95)
at android.os.Looper.loop(Looper.java:135)
at android.app.ActivityThread.main(ActivityThread.java:5254)
at java.lang.reflect.Method.invoke(Native Method)
at java.lang.reflect.Method.invoke(Method.java:372)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:903)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:698)
Caused by: java.lang.RuntimeException: Failed to resolve attribute at index 5
at android.content.res.TypedArray.getColorStateList(TypedArray.java:425)
at android.widget.TextView.<init>(TextView.java:991)
at android.widget.Button.<init>(Button.java:111)
at android.widget.Button.<init>(Button.java:107)
at android.support.v7.widget.AppCompatButton.<init>(AppCompatButton.java:60)
at android.support.v7.widget.AppCompatButton.<init>(AppCompatButton.java:56)
at android.support.v7.internal.app.AppCompatViewInflater.createView(AppCompatViewInflater.java:97)
at android.support.v7.app.AppCompatDelegateImplV7.createView(AppCompatDelegateImplV7.java:782)
at android.support.v7.app.AppCompatDelegateImplV7.onCreateView(AppCompatDelegateImplV7.java:810)
at android.support.v4.view.LayoutInflaterCompatHC$FactoryWrapperHC.onCreateView(LayoutInflaterCompatHC.java:44)
at android.view.LayoutInflater.createViewFromTag(LayoutInflater.java:725)
at android.view.LayoutInflater.rInflate(LayoutInflater.java:806)
at android.view.LayoutInflater.rInflate(LayoutInflater.java:809)
at android.view.LayoutInflater.inflate(LayoutInflater.java:504)
at android.view.LayoutInflater.inflate(LayoutInflater.java:414)
at android.view.LayoutInflater.inflate(LayoutInflater.java:365)
at android.support.v7.app.AppCompatDelegateImplV7.setContentView(AppCompatDelegateImplV7.java:249)
at android.support.v7.app.AppCompatDialog.setContentView(AppCompatDialog.java:75)
at android.support.v7.app.AlertController.installContent(AlertController.java:216)
at android.support.v7.app.AlertDialog.onCreate(AlertDialog.java:240)
at android.app.Dialog.dispatchOnCreate(Dialog.java:373)
at android.app.Dialog.show(Dialog.java:274)
at android.support.v7.app.AlertDialog$Builder.show(AlertDialog.java:902)
at com.example.test.testaccentcolor.MainActivity.onOptionsItemSelected(MainActivity.java:37)
at android.app.Activity.onMenuItemSelected(Activity.java:2885)
at android.support.v4.app.FragmentActivity.onMenuItemSelected(FragmentActivity.java:353)
at android.support.v7.app.AppCompatActivity.onMenuItemSelected(AppCompatActivity.java:144)
at android.support.v7.internal.view.WindowCallbackWrapper.onMenuItemSelected(WindowCallbackWrapper.java:99)
at android.support.v7.app.AppCompatDelegateImplV7.onMenuItemSelected(AppCompatDelegateImplV7.java:538)
at android.support.v7.internal.view.menu.MenuBuilder.dispatchMenuItemSelected(MenuBuilder.java:802)
at android.support.v7.internal.view.menu.MenuItemImpl.invoke(MenuItemImpl.java:153)
at android.support.v7.internal.view.menu.MenuBuilder.performItemAction(MenuBuilder.java:949)
at android.support.v7.internal.view.menu.MenuBuilder.performItemAction(MenuBuilder.java:939)
at android.support.v7.internal.view.menu.MenuPopupHelper.onItemClick(MenuPopupHelper.java:187)
at android.widget.AdapterView.performItemClick(AdapterView.java:305)
at android.widget.AbsListView.performItemClick(AbsListView.java:1146)
at android.widget.AbsListView$PerformClick.run(AbsListView.java:3053)
at android.widget.AbsListView.run(AbsListView.java:3860)
at android.os.Handler.handleCallback(Handler.java:739)
at android.os.Handler.dispatchMessage(Handler.java:95)
at android.os.Looper.loop(Looper.java:135)
at android.app.ActivityThread.main(ActivityThread.java:5254)
at java.lang.reflect.Method.invoke(Native Method)
at java.lang.reflect.Method.invoke(Method.java:372)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:903)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:698)
当然,复制颜色值(或创建颜色资源并引用它)是可行的...但是由于项目结构,我需要这种方式。
总结一下:我假设对 ?attr/colorAccent
的引用以某种方式创建了一个循环...但我不知道为什么或如何解决它。
有什么想法吗?
更新:为什么我need/want只在主题中设置accentColor
?
我们有一个许多应用程序通用的库,它定义了 "base" 必须派生实际应用程序主题的主题。所以上面的例子,经过简化,实际上更类似于这样的东西:
<!-- Styles that are inherited by application-generated styles. -->
<style name="Theme.Library.Dark" parent="@style/Theme.AppCompat">
<item name="alertDialogTheme">@style/Theme.Library.Dark.AlertDialog</item>
<!-- more properties -->
</style>
<style name="Theme.Library.Dark.AlertDialog" parent="@style/Theme.AppCompat.Dialog.Alert">
<item name="colorAccent">?attr/colorAccent</item>
<!-- more properties -->
</style>
(加上 Light 和 Light with Dark Action Bar 的相应变体)。
在此之后,应用程序项目应该只从提供的列表中选择一个基本主题,然后有类似的东西:
<!-- Application theme -->
<style name="ApplicationTheme" parent="Theme.Library.Dark">
<-- Ohter material colors -->
<item name="colorAccent">#FF9800</item>
</style>
至少这是理想的情况,以前有效(正如@Vikram 在他的回答中指出的那样,显然只是因为有两个不同的 colorAccent
属性)。
这个方案的优点是我不会被迫在库主题中为 colorAccent
提供默认值,因为它们是从 AppCompat 基类继承的。
此外,如果我需要创建颜色资源,我需要其中两个(用于浅色和深色变体),因为默认颜色不同。
因此,理想情况下,我正在寻找类似的解决方案(前提是存在)。
我相信 ?attr/colorAccent
这里指的是您要设置的那个:
<item name="colorAccent">?attr/colorAccent</item>
您可以将强调色提取到 @color
属性,然后将其用于应用程序主题和对话框主题。
colors.xml
<color name="accent">#FF9800</color>
styles.xml
<style name="AppTheme" parent="Theme.AppCompat">
<item name="colorAccent">@color/accent</item>
<item name="alertDialogTheme">@style/AlertDialogTheme</item>
</style>
<style name="AlertDialogTheme" parent="Theme.AppCompat.Dialog.Alert">
<item name="colorAccent">@color/accent</item>
</style>
<item name="colorAccent">?attr/colorAccent</item>
您已经知道这是行不通的。原因如下:
ssize_t ResTable::Theme::getAttribute(uint32_t resID, Res_value* outValue,
uint32_t* outTypeSpecFlags) const
{
int cnt = 20;
if (outTypeSpecFlags != NULL) *outTypeSpecFlags = 0;
do {
....
....
if (type == Res_value::TYPE_ATTRIBUTE) {
if (cnt > 0) {
cnt--;
resID = te.value.data;
continue;
}
ALOGW("Too many attribute references, stopped at: 0x%08x\n", resID);
return BAD_INDEX;
}
....
....
} while (true);
return BAD_INDEX;
}
该片段不言自明。你可以拥有:
<item name="colorZ" >?attr/colorY</item>
然后:
<item name="colorY" >?attr/colorX</item>
..... ....
但是像这样的链接属性不能超过 20 层深。 Android 因为 Too many attribute references...
放弃了。
在您的例子中,您是通过尝试读取属性的当前值来设置属性的值。 Android 去寻找,却找到了另一个参考——无尽的追求。
为什么之前可以用?
<style name="AppTheme" parent="@style/Theme.AppCompat">
<item name="colorAccent">#FF9800</item>
<item name="android:alertDialogTheme">@style/AlertDialogTheme</item>
</style>
<style name="AlertDialogTheme" parent="android:Theme.Material.Dialog.Alert">
<item name="android:colorAccent">?attr/colorAccent</item>
</style>
在 AppTheme
中,您正在设置支持库的 colorAccent
属性(没有 android: 命名空间)。在 AlertDialogTheme
下,您将 android:colorAccent
设置为 支持库的 colorAccent
。这就是无限循环没有形成的原因。
?attr/colorAccent
在创建 AlertDialog
时传递的 Context
中解析。为此,针对<item name="colorAccent">#FF9800</item>
.
一种可能的解决方法是在 res/values/attrs.xml
:
attr
<attr name="alertDialogColorAccent" format="reference|color"/>
现在,我们可以在AppTheme
下初始化这个属性:
<style name="AppTheme" parent="@style/Theme.AppCompat">
<item name="colorAccent">#FF9800</item>
<item name="alertDialogColorAccent" >?attr/colorAccent</item>
<item name="android:alertDialogTheme">@style/AlertDialogTheme</item>
</style>
并在AlertDialogTheme
中使用它:
<style name="AlertDialogTheme" parent="android:Theme.Material.Dialog.Alert">
<item name="colorAccent">?attr/alertDialogColorAccent</item>
</style>
但是,这仍然行不通。它仍然会创建一个循环 - colorAccent
是 alertDialogColorAccent
& alertDialogColorAccent
是 colorAccent
。在链的某处,需要设置实际颜色值:
<style name="AppTheme" parent="@style/Theme.AppCompat">
<item name="colorAccent">#FF9800</item>
<item name="alertDialogColorAccent" >#FF9800</item>
<item name="android:alertDialogTheme">@style/AlertDialogTheme</item>
</style>
现在,您可以用 alertDialogColorAccent
初始化 colorAccent
,因为它指的是实际颜色值:
<style name="AlertDialogTheme" parent="android:Theme.Material.Dialog.Alert">
<item name="colorAccent">?attr/alertDialogColorAccent</item>
</style>
基于@Vikram 的出色回答,我最终得到了这个(我把它放在一起以供将来参考,以防有人对实际解决方案感兴趣)。
对于基本样式文件,在 values
中(注意:不同的 XML 属性、颜色和 c 文件是更可取的——为了紧凑,它们都放在一起):
<?xml version="1.0" encoding="utf-8"?>
<resources>
<style name="BaseAppTheme" parent="Theme.AppCompat.Light.DarkActionBar" />
<attr name="dialogColorAccent" />
<color name="myAccentColor">#FF9800</color>
<style name="AppTheme" parent="BaseAppTheme">
<item name="dialogTheme">@style/DialogTheme</item>
<item name="alertDialogTheme">@style/AlertDialogTheme</item>
<item name="colorAccent">@color/myAccentColor</item>
<item name="dialogColorAccent">@color/myAccentColor</item>
</style>
<style name="DialogTheme" parent="Theme.AppCompat.Light.Dialog">
<item name="colorAccent">?attr/dialogColorAccent</item>
</style>
<style name="AlertDialogTheme" parent="Theme.AppCompat.Light.Dialog.Alert">
<item name="colorAccent">?attr/dialogColorAccent</item>
</style>
</resources>
新的 dialogColorAccent
属性是必需的,以便可以从对话框样式中引用它。不幸的是,它不能引用 colorAccent
本身(反过来也不可能)所以两者必须具有相同的值(在这种情况下,是对颜色资源的引用)。
这可确保使用 android.support.v7.app.AlertDialog.Builder
构建的 AlertDialogs 对所有 Android 版本的 positive/negative 按钮使用强调色。
但是,某些对话框(例如 DatePickerDialog
、TimePickerDialog
和 ProgressDialog
)在 Lollipop 中具有 material 版本但没有 AppCompat 等效项,因此需要额外的文件迄今为止。因此,以下样式文件进入 values-v21
:
<?xml version="1.0" encoding="utf-8"?>
<resources>
<style name="BaseAppTheme" parent="Theme.AppCompat.Light.DarkActionBar">
<item name="android:dialogTheme">@style/DialogTheme</item>
</style>
</resources>
这将使这些新对话框也具有强调色(但显然仅限于 Lollipop)。
解决方案可能看起来有点复杂(从 AlertDialogTheme
样式引用 @myAccentColor
更简单 :))但这是必要的,因为库中定义了多个主题,而且这个想法是这些主题可以被继承并以最小的变化使用。
我在对话框主题中将 android:textColor
设置为 @null
,这很有帮助。
styles.xml
<style name="AlertDialogTheme" parent="@style/Theme.AppCompat.Light.Dialog.Alert">
<!-- Used for the buttons -->
<item name="colorAccent">@color/colorAccent</item>
<item name="android:textColor">@null</item>
</style>
此外,这个 trick
帮助我解决了其他按钮的 android:textColor
问题。
<style name="Button.Base.Borderless" parent="Widget.AppCompat.Button.Borderless.Colored">
<item name="android:textColor">@null</item>
</style>
some_layout.xml
<Button
style="@style/Button.Base.Borderless"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/hint" />
现在按钮文本颜色是 colorAccent
在 AppTheme
<style name="AppTheme" parent="@style/Theme.AppCompat.Light.NoActionBar">
<item name="colorAccent">@color/colorAccent</item>
<item name="borderlessButtonStyle">@style/Button.Base.Borderless</item>
<item name="alertDialogTheme">@style/AlertDialog</item>
</style>