【问题标题】:Why doesn't setEnabled(false) trigger a ColorStateList for theme Theme.AppCompat.Light?为什么 setEnabled(false) 不触发主题 Theme.AppCompat.Light 的 ColorStateList?
【发布时间】:2019-04-03 22:32:11
【问题描述】:

我正在尝试在 Button 上使用 setEnabled(false) 来显示灰色禁用状态...

  • 如果我在按钮上强制使用主题,我只能得到想要的结果

    android:theme="@style/Button"
    

    然后将该 Button 样式上的父级更改为 Theme.AppCompat.Light.DarkActionBar 以外的其他内容,例如来自 Material:

    <style name="Button" parent="android:Theme.Material.Light.NoActionBar">
    

    (即使那样,只有按钮背景会改变,文本颜色不会。)

  • 如果我让按钮采用默认设置,当设置为禁用时,我无法看到按钮为灰色。 (我的理解是在使用Theme.AppCompat.时,Button变成AppCompat.Widget.Button并继承了ColorStateList。)

在我的res\values\styles.xml

    <resources>
        <!-- Base application theme. -->
        <style name="AppTheme" parent="Theme.AppCompat.Light.DarkActionBar">
            <!-- Customize your theme here. -->
            <item name="colorPrimary">@color/colorPrimary</item>
            <item name="colorPrimaryDark">@color/colorPrimaryDark</item>
            <item name="colorAccent">@color/colorAccent</item>
        </style>        
        <!-- only way I could get this to work -->
        <style name="Button" parent="android:Theme.Material.Light.NoActionBar">
            <!--<item name="colorAccent">@color/butt</item>-->
            <!--<item name="colorButtonNormal">@color/butt</item>-->
        </style>        
        <style name="AppTheme.NoActionBar">
            <item name="windowActionBar">false</item>
            <item name="windowNoTitle">true</item>
        </style>        
        <style name="AppTheme.AppBarOverlay" parent="ThemeOverlay.AppCompat.Dark.ActionBar" />
        <style name="AppTheme.PopupOverlay" parent="ThemeOverlay.AppCompat.Light" />
    </resources>

AndroidManifest.xml

    <application
    android:theme="@style/AppTheme"

    ( and nothing under <activity> )

我的MainActivity 扩展了Activity

  • 但我也尝试过扩展 AppCompatActivity 并遇到了同样的问题。

那么这里发生了什么?为什么“禁用按钮”的默认设置不起作用?

  • (并且如果我确实继承了 Material 主题(如上),为什么按钮文本颜色没有改变?基本上,为什么按钮看起来不像它们在主题中所做的那样编辑?)

还有

  • 当我通过res\color 指定自定义ColorStateList 时,为什么我不能只覆盖某些状态?我试图只有

    <item android:state_enabled="false" android:color="#616161" />
    

    但它没有用。如果我指定更多&lt;item&gt;s,我只能使用此方法获得一个灰色的禁用按钮,即使我不关心匹配其他状态。仅当我添加以下内容时才有效:

    <!-- disabled state -->
    <item android:state_enabled="false" android:color="#616161" />    
    <!-- enabled state -->
    <item android:state_enabled="true" android:color="@color/colorPrimaryLighter" />    
    <!-- default -->
    <item android:color="#ffffff"/>
    

我的supportLibraryVersion = '27.1.1'

我已经看过了

How to setup disabled color of button with AppCompat?

Widget.AppCompat.Button colorButtonNormal shows gray

State list drawable and disabled state

Android - default button style

Android: change color of disabled text using Theme/Style?

When should one use Theme.AppCompat vs ThemeOverlay.AppCompat?

Use android:theme and ThemeOverlay to theme specific Views and their descendents Pro-tip by +Ian Lake

Nesting Android Themes

如果我不需要,我不想覆盖默认值并指定我不需要的任何内容。提前致谢!

【问题讨论】:

    标签: android android-layout android-appcompat


    【解决方案1】:

    让我更详细地介绍一下,希望能帮助您解决一些问题:

    激活默认材质按钮:

    旁注:总的来说,我认为在使用 AppCompat 主题同时使用 AppCompatActivity 等时,它会导致更一致的外观和行为。

    但正如您注意到的那样,仅使用 AppCompat 主题,Button(内部映射到 android.support.v7.widget.AppCompatButton)不会自动应用兼容性材料样式。

    我不确定这是否是一个错误,但我怀疑这是为了保持外观向后兼容。您可以通过直接在视图上应用正确的样式轻松激活它:

    <Button
       style="@style/Widget.AppCompat.Button.Colored"
       ... />
    

    这是一个屏幕截图,显示了启用和禁用按钮的不同外观。 样式按钮是前两个。活动的有粉红色的强调色作为背景。 旧的 Android 按钮是底部的两个。如您所见,禁用只会改变文本颜色和略微改变轮廓。

    如果您希望将其应用于您在应用中使用的任何按钮,您可以将以下行添加到您的AppTheme

    <style name="AppTheme" parent="Theme.AppCompat.Light.DarkActionBar">
       <item name="buttonStyle">@style/Widget.AppCompat.Button.Colored</item>
       ...
    

    这将使用您的默认 colorAccent 作为按钮突出显示颜色,并使用灰色调作为平面禁用按钮。您提到仅在清单中为您的App 定义android:theme。要使此解决方案起作用,您可能需要将主题设置为每个 Activity

    定义不同状态的颜色

    ColorStateList 的想法是定义一种颜色,该颜色根据View 的状态而变化。如果特定定义的条件不匹配,它需要默认情况作为后备。

    您的示例可以简化为:

    <!-- disabled state -->
    <item android:state_enabled="false" android:color="#616161" />
    <!-- default = enabled state -->
    <item android:color="@color/colorPrimaryLighter"/>
    

    对于按钮,您还可以使用默认的 disabled alpha 属性,例如定义button_accent_stated =

    <selector xmlns:android="http://schemas.android.com/apk/res/android">
        <item android:state_enabled="false"
              android:alpha="?android:attr/disabledAlpha"
              android:color="@color/colorAccent"/>
        <item android:color="@color/colorAccent" />
    </selector>
    

    自定义颜色样式

    如果您对默认设置感到满意,那么您可以忽略以下部分...

    如果您想为按钮设置样式(其他组件也一样)并为其赋予自定义颜色,您需要在应用样式和主题之间做出区别。两者都在styles.xml 中定义,因此可能会造成混淆。我可以建议给定义名称表示它们的使用方式。

    当您想要覆盖例如强调色时,您可以创建一个自定义ButtonTheme 并使用android:theme 应用它,如您的问题所示。

    当您想要调整组件的外观时(例如,使用材质按钮来获得不同的禁用颜色),您可以创建一个自定义ButtonStyle 并使用style 应用它。这些定义需要一个与View 对应的parent

    使用材质按钮和最新的支持库,以前使用 colorButtonNormal 设置禁用颜色的方法似乎不再适用。相反,您可以通过创建自定义样式并将其设置为backgroundTint 来使用上一步中定义的颜色。

    <style name="AppTheme.ButtonStyle" parent="@style/Widget.AppCompat.Button.Colored">
        <item name="colorAccent">@color/colorAccent</item>
        <item name="android:backgroundTint">@color/button_accent_stated</item>
    </style>
    

    这将导致这样的禁用按钮:

    您可以再次在AppTheme 中按按钮或整体应用上述样式。如果您仍然需要支持 pre-lollipop 设备,您可能还需要自定义主题来设置 colorButtonNormal

    更新:默认材质 + 规定的文字颜色:

    玩过我的示例应用程序,我发现阻止默认声明文本颜色的唯一其他解释是在您的 AppTheme 上定义 android:textColorPrimary

    <item name="android:textColorPrimary">@color/colorPrimaryTextOnLight</item>
    

    如果您有所有没有自定义文本颜色的按钮,则这两种状态都将使用此文本!见Screenshot

    如果你只是想修复按钮文本来改变颜色,你可以像这样定义你的按钮样式:

    <style name="AppTheme.TextStatedButtonStyle" parent="@style/Widget.AppCompat.Button.Colored">
        <item name="android:textColor">@color/button_text_stated</item>
    </style>
    

    使用button_text_stated 这样的选择器:

    <selector xmlns:android="http://schemas.android.com/apk/res/android">
        <item android:state_enabled="false"
              android:alpha="?android:disabledAlpha"
              android:color="@color/colorPrimaryTextOnLight" />
        <item android:color="@color/colorPrimaryTextOnDark" />
    </selector>
    

    只需将buttonStyle 应用于所有按钮或将style 应用于每个按钮,如上所示。

    我已将我的demo project 上传到 Github,其中包含更多的屏幕截图/设置样式和主题的组合,因为我觉得这个答案太长了,最好只是玩一下以了解样式的工作原理。

    在 Android Studio 中预览

    正如 cmets 中提到的,我可以确认 Android Studio 中的预览有时会显示错误的元素,尤其是在将自定义主题应用于元素时!最好在模拟器或真机上进行测试。

    【讨论】:

    • 感谢您的回答!我仍然有同样的问题。为了澄清我不是想给按钮上色,只是想做setEnabled(false) 导致该状态的“灰色”背景和文本颜色,例如屏幕截图的下半部分。如上所述,我通过使用android:theme="@style/Button&lt;style name="Button" parent="android:Theme.Material.Light.NoActionBar"&gt; 设置按钮的样式来实现背景颜色更改,但 文本颜色 不会改变。按照建议使用ColorStateList 无济于事。
    • 您能否进一步解释以下几点:(1)“通过使用 AppCompat 主题,按钮(内部映射到 android.support.v7.widget.AppCompatButton)不会自动应用兼容性材质样式”,以及(2 )“要使此解决方案起作用,您可能需要为每个活动设置主题。” - 为什么? ...如果它是从清单的&lt;Application&gt; 继承的。
    • 总而言之,我试图尽可能使用默认值(并尽可能少地更改)以实现“禁用按钮外观”,同时还注意到我的 MainActivity 扩展了 Activity 和不是AppCompat...
    • 总结一下我的尝试:(如前所述,)如果我在Buttonparent="android:Theme.Material.Light.NoActionBar" 上使用样式,那么禁用外观大部分是可以接受的,但是文本颜色 没有改变...但是如果我将&lt;item name&gt; 添加到这种样式中(对于colorAccentcolorButtonNormalandroid:backgroundTint,它们使用您提供的状态指向ColorStateList)然后背景颜色会改变,但外观实际上更糟!按钮现在是彩色的,但状态enabled false的背景颜色是错误的,而且文本根本没有改变
    • "使用材质按钮和最新的支持库,以前使用 colorButtonNormal 设置禁用颜色的方法似乎不再起作用了。" --> 非常感谢,我以为我要疯了它不工作!
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-11-30
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-12-08
    • 2021-12-01
    相关资源
    最近更新 更多