我正在尝试设置AppCompat的对话框,以便按钮使用与应用程序的强调颜色相同的颜色,而不重复颜色本身。通过在values-v21
中使用此样式文件,这与AppCompat v22(显然只适用于棒棒糖)完美配合:
<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版本设置此设置时,因此我将这些样式移到了base
values
文件夹中,并基本上用它们的appcompat对应项替换了所有特定于android:
和v21的属性。<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$3.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$3.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
的引用以某种方式创建了一个循环…但我不知道为什么或如何修复它。有什么想法吗?
更新:为什么我只需要/想要在主题中设置
accentColor
我们有一个对许多应用程序都很常见的库,它定义了必须从中派生实际应用程序主题的“基本”主题。所以上面的例子,简化了,实际上更像这样:
<!-- 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>
(加上相应的灯光变种和带有深色操作栏的灯光变种)。
在此之后,应用程序项目应该只从提供的列表中选择一个基本主题,然后具有如下内容:
<!-- Application theme -->
<style name="ApplicationTheme" parent="Theme.Library.Dark">
<-- Ohter material colors -->
<item name="colorAccent">#FF9800</item>
</style>
至少这是以前的理想情况(正如@vikram在他的回答中指出的,显然只是因为有两个不同的
colorAccent
属性)。此方案的优点是,我不会被迫为库主题中的
colorAccent
提供默认值,因为这些值是从appcompat基主题继承的。此外,如果我需要创建颜色资源,我将需要其中两个(用于明暗变体),因为默认颜色不相同。
所以,理想情况下,我正在寻找一个类似的解决方案(如果存在的话)。
最佳答案
<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...
而放弃。在您的例子中,您通过尝试读取属性的当前值来设置属性的值。安卓开始寻找,但找到了另一个参考-无休止的追求。
为什么它以前起作用?
<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:namespace)。在AlertDialogTheme
下,将android:colorAccent
设置为支持库的colorAccent
。这就是为什么一个无休止的循环没有形成的原因。创建
?attr/colorAccent
时,会在通过的Context
中解析AlertDialog
。因此,它是针对<item name="colorAccent">#FF9800</item>
解决的。一个可能的解决方法是在
attr
中定义一个自定义的res/values/attrs.xml
<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>
并将其用于
<style name="AlertDialogTheme" parent="android:Theme.Material.Dialog.Alert">
<item name="colorAccent">?attr/alertDialogColorAccent</item>
</style>
但是,这仍然不起作用。它仍然创建一个循环-
AlertDialogTheme
iscolorAccent
&alertDialogColorAccent
isalertDialogColorAccent
。在链的某个位置,需要设置实际的颜色值:<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>
现在,可以用
colorAccent
初始化colorAccent
,因为它引用了实际的颜色值:<style name="AlertDialogTheme" parent="android:Theme.Material.Dialog.Alert">
<item name="colorAccent">?attr/alertDialogColorAccent</item>
</style>