【问题标题】:Inheriting AppCompat 22.1.1 Dialog colorAccent from app theme doesn't work从应用程序主题继承 AppCompat 22.1.1 对话框 colorAccent 不起作用
【发布时间】:2015-07-19 20:52:28
【问题描述】:

我正在尝试设置 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$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>

(加上 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 基本主题继承的。

另外,如果我需要创建颜色资源,我需要其中两个(用于浅色和深色变体),因为默认颜色不同。

因此,理想情况下,我正在寻找类似的解决方案(前提是存在)。

【问题讨论】:

    标签: android android-support-library android-styles android-appcompat


    【解决方案1】:

    我相信?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>
    

    【讨论】:

    • 是的,我知道这是一种可能性,但我正在寻求其他解决方案。还是谢谢。
    【解决方案2】:
    <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;
    }
    

    sn-p 是不言自明的。您可以:

    <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 内解析。因此,它针对&lt;item name="colorAccent"&gt;#FF9800&lt;/item&gt; 解决。

    一种可能的解决方法是在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>
    

    但是,这仍然行不通。它仍然会创建一个循环 - colorAccentalertDialogColorAccentalertDialogColorAccentcolorAccent。沿着链的某个地方,需要设置一个实际的颜色值:

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

    【讨论】:

    • 几乎是我需要的解决方案,但不完全是。我曾尝试过类似的东西,根据我的测试,如果我尝试在AppTheme 中设置&lt;item name="alertDialogColorAccent"&gt;?attr/colorAccent&lt;/item&gt;,它就不起作用。你能证实这一点吗?我的目标是在库中提供一些自定义主题,只需在其中设置 colorAccent 即可对其进行自定义。
    • @matiash 我明白了。让我们在这里谈谈:Link
    【解决方案3】:

    基于@Vikram 的出色回答,我得出了这个结论(我将其放在以供将来参考,以防有人对实际解决方案感兴趣)。

    对于基本样式文件,在values 中(注意:属性、颜色和 c 的不同 XML 文件更可取——为了紧凑,它们都放在一起):

    <?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 版本的正/负按钮使用强调色。

    但是,对于某些对话框(例如 DatePickerDialogTimePickerDialogProgressDialog),在 Lollipop 中具有材料版本但目前还没有 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>
    

    这将使这些新对话框也具有强调色(但显然只在棒棒糖中)。


    解决方案可能看起来有点复杂(从AlertDialogTheme 样式引用@myAccentColor 更简单:))但这是必要的,因为在库中定义了多个主题,并且想法是这些主题可以被继承并且只需极少改动即可使用。

    【讨论】:

      【解决方案4】:

      我在对话主题中将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>
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2014-12-20
        • 2015-09-18
        • 2018-01-15
        • 1970-01-01
        • 1970-01-01
        • 2016-01-11
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多