Why doesn't setEnabled(false) trigger a ColorStateList for theme Theme.AppCompat.Light?

折月煮酒 提交于 2021-02-08 01:18:12

问题


I am trying to use setEnabled(false) on a Button to show grey disabled state...

  • I can only get the desired result if I force a theme on the button with

    android:theme="@style/Button"
    

    and then change the parent on that Button style to something other than the Theme.AppCompat.Light.DarkActionBar, such as from Material:

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

    (even then, only the button background changes, the text color doesn't.)

  • I can't see the Button as grey when set disabled if I leave the Button to take the defaults. (My understanding is that when using Theme.AppCompat., Button becomes AppCompat.Widget.Button and inherits a ColorStateList.)

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

In AndroidManifest.xml

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

    ( and nothing under <activity> )

My MainActivity extends Activity

  • but I have also tried extends AppCompatActivity and had the same problem.

So what is going on here? Why doesn't the default work for a "disabled button"?

  • (And if I do inherit from Material theme (as above), why does the Button text color not change? Basically, why don't the Buttons look like they do in the theme editor?)

Also

  • when I specify a custom ColorStateList via res\color, why can't I override just some of the states? I tried to have only

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

    But it didn't work. I was only able to get a greyed disable Button using this method if I specified more <item>s, even though I don't care to match other states. It only works If I add something like:

    <!-- 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"/>
    

My supportLibraryVersion = '27.1.1'

I have already seen

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

and Nesting Android Themes

I don't want to override the defaults if I don't have to and specify anything I don't have to. Thanks in advance!


回答1:


Let me go into more detail to hopefully help clear up some of your questions:

Activating default material buttons:

Side note: Overall I think it leads to a more consistent look and behaviour when using the AppCompat Theme to also use AppCompatActivity etc.

But as you noticed just by using the AppCompat Theme, a Button (Internally mapped to android.support.v7.widget.AppCompatButton) doesn't automatically apply the compatibility material style.

I'm not sure if this is a bug, but I suspect it's to keep the look backwards compatible. You can easily activate it by applying the correct style directly on the view:

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

Here is a screenshot to show the different look of enabled and disabled buttons. The styled buttons are the first two. The active one has the pink accent color as background. The old Android button are the bottom two. As you can see disable only changes the text color and slightly the outline.

If you want this applied to any button you use in your app, you can add the following line to your AppTheme:

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

This will use your default colorAccent as button highlight color and a grey tone as a flat disabled button. You mentioned only defining the android:theme for your App in your Manifest. For this solution to work you might need to set the theme to each Activity.

Defining a color with different states

The idea of the ColorStateList is to define a color that changes depending on the state of a View. It needs the default case as a fallback in case the conditions of the specific definitions don't match.

Your example can be simplified to the following:

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

For buttons you can also make use of the default disabled alpha attribute e.g. define 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>

Custom color styling

If you are happy with the defaults, then you can ignore the following part...

If you want to style a button (and same for other components) and give it custom colors you need to make the difference between applying styles and themes. Both are defined in the styles.xml so this can be confusing. I can recommend giving the definitions names that represent how they are used.

When you want to overwrite for example the accent color, you create a custom ButtonTheme and apply it with android:theme as shown in your question.

When you want to adjust the look of a component (e.g. with the material buttons to get a different disabled color), you create a custom ButtonStyle and apply it with style. These definitions need a parent that corresponds to the View.

With material buttons and latest support libraries the previous approach to set the disabled color with colorButtonNormal seems to not work anymore. Instead you can use the color defined in the previous step by creating a custom style and set it as 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>

This will lead to a disabled button like this:

Again you can apply this style as above per button or overall in your AppTheme. If you still need to support pre-lollipop devices you probably will also need a custom theme to set the colorButtonNormal.

Update: Default Material + stated text color:

Played with my example app and the only other explanation I found to what prevents the default stated text color is defining android:textColorPrimary on your AppTheme!

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

If you have this all buttons without a custom text color will use this text for both states! See Screenshot.

If you just want to fix the button text to change color, you can define your button style like this:

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

With button_text_stated a selector like this:

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

And simply apply this buttonStyle for all buttons or as style per button as shown above.

I've uploaded my demo project with more screenshots / combinations of setting styles and themes to Github since I felt this answer was getting too long and it's better to just play with it to understand how styles work.

Preview in Android studio

As mentioned in the comments I can confirm that the Preview in Android studio is sometimes showing elements wrong, especially when applying a custom theme to an element! Better to test in the emulator or on a real device.



来源:https://stackoverflow.com/questions/53071828/why-doesnt-setenabledfalse-trigger-a-colorstatelist-for-theme-theme-appcompat

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!