Selector drawable on MaterialButton android - android

I have been using the new MaterialButton class. I want different colors on the button when the user clicks the button.
I have been using selector drawables for this purpose since the very beginning, however it doesn't appear to be working on MaterialButton.
<com.google.android.material.button.MaterialButton
android:layout_width="0dp"
android:text="#string/login"
style="#style/Widget.Mohre.Button"
android:layout_height="wrap_content"
app:cornerRadius="#dimen/card_corner_radius"
android:padding="#dimen/unit_large"
android:id="#+id/loginBtn"/>
My Widget.Mohre.Button style
#color/textColorWhite
#drawable/mohre_button_selector
#style/TextAppearance.Button
#animator/button_state_list_anim
My selector drawable
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:drawable="#drawable/mohre_button_pressed" android:state_pressed="true" />
<item android:drawable="#drawable/mohre_button_selected" android:state_enabled="false" android:state_pressed="false" />
<item android:drawable="#drawable/mohre_button_normal" />
My individual drawables are just rectangle shapes with different colors like these
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<corners android:radius="30dp"></corners>
<solid android:color="#3a516a"></solid>
</shape>
The button doesn't take on the colors at all from the selector drawable. It just shows the default accent color of the application

With the normal way (setting the selector drawable as background of the button), it won't work as expected if you are using Theme.MaterialComponents.
You can use Theme.AppCompat.DayNight.NoActionBar as an alternative, but if you don't want to use that theme, then simply use:
<androidx.appcompat.widget.AppCompatButton
................/>
You will get the same result as it was working with the Appcompat theme even if you are using latest Material themes!

as explained in this medium post https://medium.com/over-engineering/hands-on-with-material-components-for-android-buttons-76fa1a92ec0a we can use ColorStateList to change color or background of Button.
For me working solution for now was to set backgroundTint to null and backgroundTintMode to add like this:
<style name="Button.XYZ" parent="Button">
<item name="shapeAppearance">?attr/shapeAppearanceLargeComponent</item>
<item name="drawableTint">#color/ColorXYZ</item>
<!-- background drawable -->
<item name="backgroundTint">#null</item>
<item name="backgroundTintMode">add</item>
<!-- background drawable -->
<item name="android:background">#drawable/XYZ</item>
<!-- Color drawable -->
<item name="android:textColor">#color/XYZ</item>
</style>
And also using the style in xml not in theme

If you want a button that has a custom background (to apply <selector> e.g), but your theme is set up to use Theme.MaterialComponents (which is nowadays, 2021), you could switch the XML element in the layout to be <android.widget.Button> instead of <Button>. This should cause the Material Components for Android to ignore that element, and you can manipulate this button normally with respect to XML attributes.

Related

How do I programmatically change the style of a Material Component Text Button in Android?

I have a material text button <Button android:id="#+id/button" style="#style/Widget.MaterialComponents.Button.TextButton"/> that I'd like to change the colour of at runtime. So I set the text colour using button.setTextColor(Color.rgb(10, 10, 10)). Unfortunately, this doesn't change the background drawable, so when I click on the button it's ripple colour is unchanged. I'm guessing I need to change the background with something like attackButton.background = getDrawable(R.drawable.ripple), but I'm not sure how to populate the ripple.xml. Does this method make sense for changing the button text colour and ripple? If so, how should I write the ripple.xml?
To change the colors in the MaterialButton you can use:
button.setBackgroundTintList to change the background tint. You should use a selector.
button.setRippleColor to change the ripple color. Also in this case you should use a selector (see below)
button.setTextColor to change the color of the text. Also in this case you should use a selector.
It is the default selector used in the ripple color:
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:alpha="#dimen/mtrl_low_ripple_pressed_alpha" android:color="?attr/colorPrimary" android:state_pressed="true"/>
<item android:alpha="#dimen/mtrl_low_ripple_focused_alpha" android:color="?attr/colorPrimary" android:state_focused="true" android:state_hovered="true"/>
<item android:alpha="#dimen/mtrl_low_ripple_focused_alpha" android:color="?attr/colorPrimary" android:state_focused="true"/>
<item android:alpha="#dimen/mtrl_low_ripple_hovered_alpha" android:color="?attr/colorPrimary" android:state_hovered="true"/>
<item android:alpha="#dimen/mtrl_low_ripple_default_alpha" android:color="?attr/colorPrimary"/>
</selector>
Have you tried a RippleDrawable? Then just Button.setBackground() to different resource or even a selector xml. If a ripple with selector won't be enough for you, setting the ripple mask itself can be done
myRipple.xml
<ripple android:color="#ffff0000">
<item android:id="#android:id/myRippleMask"
android:drawable="#android:color/white" />
</ripple>
programmatically:
LayerDrawable myRipple = ContextCompat.getDrawable(context, drawable.myRipple.xml);
myRipple.setDrawableByLayerId(R.id.myRippleMask,Color.rgb(10, 10, 10));
with each mask being a different color
One line of code programmatic no resource file methods for textcolor, backgroundcolor, and ripplecolor:
MaterialButton myMaterialButton = new MaterialButton(this);
myMaterialButton.setTextColor(Color.RED);
myMaterialButton.setBackgroundColor(Color.GRAY);
myMaterialButton.setRippleColor(ColorStateList.valueOf(Color.RED));

How to color button background when it is disabled

I want to color my button that is defined in a fragment. I created new style (which I use as a theme in the button) and defined "colorAccent" for enabled state, "colorButtonNormal" for disabled and parent of this style is "Widget.AppCompat.Button". I want it to be coloured exactly as it is written in colorButtonNormal when button is disabled.
<style name="Material.Button.Primary" parent="#style/Widget.AppCompat.Button">
<item name="android:colorButtonNormal">#color/color_disabled</item>
<item name="colorAccent">#color/color_primary</item>
</style>
<Button
style="#style/Widget.AppCompat.Button.Colored"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="#string/button_text"
android:textAppearance="#style/AppTheme.Text"
android:theme="#style/Material.Button.Primary" />
When the button is enabled it has correct color from colorAccent. When user clicks on it, it becomes disabled and should be gray (#b2b2b2) but it becomes a little bit lighter (#E7E7E7). It seems like it takes color that I defined and mixes with white color.
I tried to change style's parent and did some changes in style and button's attributes as it is written in some guides from the internet but nothing worked. My current solution is to set colorButtonNormal to #000000. When button is disabled, it becomes #B9B9B9.
Forgive me if I am wrong but as far I understand correctly, you want to achieve different colours for enabled/disabled states.
UPDATE
For keeping the material effect you can use styling with Widget.AppCompat.Button.Colored and create theme with specified colours:
<Button
.
.
.
style="#style/Widget.AppCompat.Button.Colored"
android:theme="#style/CustomButton"/>
And create theme, where colorButtonNormal is for disabled state
<style name="CustomButton" parent="Widget.AppCompat.Button.Colored">
<item name="colorButtonNormal">#color/color_disabled</item>
<item name="colorAccent">#color/color_enabled</item>
</style>
Old without material effect
You can try using selector like:
custom_button.xml
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:state_enabled="false" android:drawable="#color/color_disabled" />
<item android:state_enabled="true" android:drawable="#color/color_enabled/>
</selector>
and then in your xml for button:
<Button
.
.
.
android:background="#drawable/custom_button" />

How to set specific color for background when being clicked on borderless button?

Short question:
Suppose I use this style on a button:
<Button
style="#style/Widget.AppCompat.Button.Borderless.Colored" ...
(or without the "Colored" part).
How do I set the background of it to be a selector that has a different color when being pressed? The default one is quite a bold color on pre-Lollipop...
I want to have all of the style working as the default (including padding), except for the color of the selector.
I think the easiest would be to create a typical multi-state drawable and replicate Android's default button drawable properties for padding, etc...
From the Android SDK AppCompat theme:
<!-- Colored bordered ink button -->
<style name="Base.Widget.AppCompat.Button.Colored">
<item name="android:background">#drawable/abc_btn_colored_material</item>
<item name="android:textAppearance">#style/TextAppearance.AppCompat.Widget.Button.Inverse</item>
</style>
They use the drawable abc_btn_colored_material, whose source is like this: https://github.com/dandar3/android-support-v7-appcompat/blob/master/res/drawable/abc_btn_borderless_material.xml
You can see it's just a layer list with the following multi-state drawable:
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:state_focused="true" android:drawable="#drawable/abc_btn_default_mtrl_shape"/>
<item android:state_pressed="true" android:drawable="#drawable/abc_btn_default_mtrl_shape"/>
<item android:drawable="#android:color/transparent"/>
</selector>
https://github.com/dandar3/android-support-v7-appcompat/blob/master/res/drawable/abc_btn_default_mtrl_shape.xml
<inset xmlns:android="http://schemas.android.com/apk/res/android"
android:insetLeft="#dimen/abc_button_inset_horizontal_material"
android:insetTop="#dimen/abc_button_inset_vertical_material"
android:insetRight="#dimen/abc_button_inset_horizontal_material"
android:insetBottom="#dimen/abc_button_inset_vertical_material">
<shape android:shape="rectangle">
<corners android:radius="#dimen/abc_control_corner_material" />
<solid android:color="#android:color/white" />
<padding android:left="#dimen/abc_button_padding_horizontal_material"
android:top="#dimen/abc_button_padding_vertical_material"
android:right="#dimen/abc_button_padding_horizontal_material"
android:bottom="#dimen/abc_button_padding_vertical_material" />
</shape>
</inset>
So just make it yours :) Copy the drawable xml into your app and customize it.
I don't know if you will be able to access the #dimen/... from outside the SDK package, those values are defined in the SDK Values.xml, take a look at https://github.com/dandar3/android-support-v7-appcompat/blob/master/res/values/values.xml . If it's not possible, just create your own version of the dimens in your values.xml
If there's a cleaner solution any android guru can point out, I'll be glad to know!
Requested Update
Another trick is to apply a ColorFilter to the whole Button View. It's a very simple thing but not useful for all situations, as the color filter will blindly change the view as a whole (including Font color, borders, etc...). However, smartly selecting Alpha on the colors, could work for specific situations.
I'd suggest a LightningColorFilter. To apply a color filter to the Button, or to any view, you can do something like
myButton.getBackground().setColorFilter(new LightingColorFilter(Color.WHITE, Color.RED));
Check out what every color means here: http://developer.android.com/reference/android/graphics/LightingColorFilter.html

How to use textColorPrimary as background color in a style?

I want to create a style which uses the android textColorPrimary as a background color.
I tried the following which does not work, the result is my layout not beeing displayed at all.
<style name="horizontalLine">
<item name="android:layout_width">fill_parent</item>
<item name="android:layout_height">1dp</item>
<item name="android:background">?android:attr/textColorPrimary</item>
</style>
How do I use textColorPrimary as background color in a style?
This syntax seems to work for me, when trying to use attributes:
<TextView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:textColor="?android:textColorPrimary"
android:text="Hello"/>
(or)
<style name="MyStyle">
<item name="android:textColor">?android:textColorPrimary</item>
</style>
I can change the app theme from Holo to Holo.Light and the text color will change automatically to fit.
It doesn't work when I set it as a background of a View though - Android will crash complaining that the drawable referenced is a state list that does not specify drawables (it is a state list of colors).
Caused by: org.xmlpull.v1.XmlPullParserException: Binary XML file line #2: <item> tag requires a 'drawable' attribute or child tag defining a drawable
at android.graphics.drawable.StateListDrawable.inflate(StateListDrawable.java:178)
at android.graphics.drawable.Drawable.createFromXmlInner(Drawable.java:885)
at android.graphics.drawable.Drawable.createFromXml(Drawable.java:822)
at android.content.res.Resources.loadDrawable(Resources.java:1950)
... 39 more
I am using HoloEverywhere which lets me reference the resources directly, but you should get a similar problem in the native case. I don't think the primary, non-selected, non-activated (etc.) color used as a component in the state list xml is exposed through an attribute.
In any case, the text color used is dependent on the theme that you, the app developer, chooses. If you choose to use the Holo (dark) theme then your text will be a light color, and the user won't be able to affect this. You don't need to make the your line color dynamic for your app.
To apply the ?android:attr/textColorPrimary attribute as the background of a view, you have two options.
First Option
The first option is to define a shape drawable which gets its color from the ?android:attr/textColorPrimary attribute, as follows...
<shape xmlns:android="http://schemas.android.com/apk/res/android" android:shape="rectangle">
<solid android:color="?android:attr/textColorPrimary" />
</shape>
... and, assuming you've named this drawable rectangle_shape_with_primary_text_color.xml and placed it in your application's res/drawable folder, you can set it as the background of your style as follows...
<item name="android:background">#drawable/rectangle_shape_with_primary_text_color</item>
... or set it as the background of your view directly, as follows...
android:background="#drawable/rectangle_shape_with_primary_text_color"
Second option
The second option is to set the backgroundTint property of your style or view.
You can set the backgroundTint property of your style as follows:
<style name="horizontalLine">
<item name="android:background">#android:color/white</item>
<item name="android:backgroundTint">?android:attr/textColorPrimary</item>
</style>
Or you can set the backgroundTint property of your view directly as follows:
<View
android:id="#+id/horizontalLine"
android:layout_width="match_parent"
android:layout_height="1dp"
android:background="#android:color/white"
android:backgroundTint="?android:attr/textColorPrimary" />
Note that the exact value of the background property with this approach is not important but it must be set and it cannot be set to #null or #android:color/transparent.
The error message I get for this is:
org.xmlpull.v1.XmlPullParserException: Binary XML file line #18: <item> tag requires a 'drawable' attribute or child tag defining a drawable
Doing a little digging, the specs say the background attribute should support either a colour, or reference to a drawable resource:
... Looking at the resource you're referencing, it is a StateListDrawable.
platforms/android-17/data/res/color/primary_text_dark.xml
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:state_enabled="false" android:color="#android:color/bright_foreground_dark_disabled"/>
<item android:state_window_focused="false" android:color="#android:color/bright_foreground_dark"/>
<item android:state_pressed="true" android:color="#android:color/bright_foreground_dark_inverse"/>
<item android:state_selected="true" android:color="#android:color/bright_foreground_dark_inverse"/>
<item android:state_activated="true" android:color="#android:color/bright_foreground_dark_inverse"/>
<item android:color="#android:color/bright_foreground_dark"/> <!-- not selected -->
</selector>
However, the docs for StateListDrawable also explicitly say the drawable attribute must be defined for item elements:
https://developer.android.com/guide/topics/resources/drawable-resource.html
<item>
Defines a drawable to use during certain states, as described by its attributes. Must be a child of a <selector> element.
attributes:
android:drawable
Drawable resource. Required. Reference to a drawable resource.
... which isn't the case for the case for primary_text_dark.xml. So, it's not working because the drawable you're referencing doesn't seem to conform to the spec.
I think the workaround is to reference the colour that's used in primary_text_dark for the default state: bright_foreground_dark. Seeing as that's not public, you need to go directly to the one it references, which is:
android:background="#android:color/background_light"
I take it that you want to use native android primary text color.
<item name="android:background">#android:color/primary_text_dark</item>

Android ImageButton background color

I have an ImageButton with a background image that has some transparency. By default, the button gets a grey background where the transparency pixels are - due to the Holo.Light theme. I tried setting the background color of the button to transparent via the setBackgroundColor(Color.TRANSPARENT) method. That works just fine and does what I need except now my button no longer has the light blue color when focused/pressed and looks rather flat (no borders around it, so it looks like an image).
I googled and saw that you can assign selectors as explained here but that would mean that I have to specify an image per button state and I don't want to do that. I want to inherit the focuses/pressed colors from the theme but overwrite the normal button background (when not pressed/focused) to transparent instead of grey. How can I achieve that?? Please provide a working example as I have tried many different combinations with no success.
Edit
Thank you all for helping. I figured out how to make this work without having to recreate the same image with the focused and pressed states for each button!
Here is my solution:
My button is defined as:
<ImageButton
android:id="#+id/ImageButton05"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:background="#drawable/button" />
And my background XML file (titled button.xml) is defined as follows:
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:state_pressed="true">
<layer-list>
<item android:drawable="#drawable/btn_default_pressed_holo_light"></item>
<item android:drawable="#drawable/network_wifi"></item>
</layer-list>
</item>
<item android:state_focused="true">
<layer-list>
<item android:drawable="#drawable/btn_default_focused_holo_light"></item>
<item android:drawable="#drawable/network_wifi"></item>
</layer-list>
</item>
<item android:state_hovered="true">
<layer-list>
<item android:drawable="#drawable/btn_default_focused_holo_light"></item>
<item android:drawable="#drawable/network_wifi"></item>
</layer-list>
</item>
<item>
<layer-list>
<item android:drawable="#drawable/btn_default_normal_holo_light"></item>
<item android:drawable="#drawable/network_wifi"></item>
</layer-list>
</item>
</selector>
You can put something into an xml file, for example, custom_button.xml and then set background="#drawable/custom_button" in the button view.
Please refer to this link as there is an xml example: Standard Android Button with a different color
I used it in my code and it worked just the way I wanted.
Reference:
Standard Android Button with a different color
Edited:
If you would rather use
Maybe you should try to set a border.
For example (Reference: Android - border for button ):
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<gradient android:startColor="#FFFFFF"
android:endColor="#00FF00"
android:angle="270" />
<corners android:radius="3dp" />
<stroke android:width="5px" android:color="#000000" />
</shape>
OR,
You could try to set the style/theme for the button. (But it would be in a separate file)
The style/theme contains the color attributes for various states of button such as focused / enabled / disabled/ etc.
For setting background color/image and having click highlight effect,
Here is an example (Reference: How to programmatically setting style attribute in a view)
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item
android:state_pressed="true"
android:drawable="#drawable/btn_pressed" />
<item
android:state_pressed="false"
android:drawable="#drawable/btn_normal" />
</selector>
You can then apply this selector to a Button by setting the property android:background="#drawable/my_button".
I want to inherit the focuses/pressed colors from the theme but overwrite the normal button background (when not pressed/focused) to transparent instead of grey. How can I achieve that??
AFAIK you can't because the focus/pressed colors are built in to the same image resources that contain the grey background.
If you want to keep the system focus/pressed but remove the background you'll have to grab a copy of the image resources (which can be found in your SDK at /sdkroot/platforms/[version]/data/res/drawable-hdpi replace [version] with whatever api level you are after. And if needbe replace hdpi with another density) and edit out the grey button from them with a photo editor. Then make a selector that references your modified images and set that as the background for your button.
EDIT:
Here are the default holo button images for focused and pressed
You can use ImageView instead of ImageButton to solve your problem. Please set the android:background property of the ImageView to the background color of the parent.
You can write this in your xml file:
android:background="#null"
It worked for me.
if u don't need the default gray background and u need your ImageButton seems like an icon without no backgrounds just use android:background attribute and set it to
#android:color/transparent**
<ImageButton
android:id="#+id/ImageButton05"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:background="#android:color/transparent"
android:background="#drawable/button" />
hope that help anybody

Categories

Resources