Android MaterialButtonGroup Style Change - android

I am in the process of building my first Android app and am running into an issue that should seemingly be very simple, but I'm at a loss on why it won't work. I am trying to use a MaterialButtonGroup where a selection is both required and only one option can be selected at a time. The XML for this is:
<com.google.android.material.button.MaterialButtonToggleGroup
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal"
app:selectionRequired="true"
android:layout_margin="10dp"
app:singleSelection="true"
app:checkedButton="#id/btnCases">
<com.google.android.material.button.MaterialButton
android:id="#+id/btnCases"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
style="#style/Theme.MyApp.Toggle.Selected"
android:onClick="#{() -> viewModel.changeScanningMode(true)}"
android:text="Cases" />
<com.google.android.material.button.MaterialButton
android:id="#+id/btnUnits"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
style="#style/Theme.MyApp.Toggle.NotSelected"
android:onClick="#{() -> viewModel.changeScanningMode(false)}"
android:text="Units" />
</com.google.android.material.button.MaterialButtonToggleGroup>
The two buttons inside represent the either/or option (scanning cases or units with a handheld scanner). There are two styles I've created - Selected and NotSelected - which have a parent style of OutlinedButton from MaterialComponents and look like this:
<style name="Theme.MyApp.Toggle.Selected" parent="Widget.MaterialComponents.Button.OutlinedButton">
<item name="android:checked">true</item>
<item name="backgroundTint">#color/dark_blue</item>
<item name="android:textColor">#color/white</item>
<item name="strokeColor">#color/dark_blue</item>
</style>
<style name="Theme.MyApp.Toggle.NotSelected" parent="Widget.MaterialComponents.Button.OutlinedButton">
<item name="android:checked">false</item>
<item name="backgroundTint">#color/white</item>
<item name="android:textColor">#color/dark_blue</item>
<item name="strokeColor">#color/dark_blue</item>
</style>
When either button is selected, I am trying to essentially swap the styles by having the Fragment this lives on using an Observer to watch for a property change on the ViewModel so it can flip the styles. The ViewModel side works fine, but swapping styles does not. The code for changing styles looks like this:
val btnCases = binding.root.findViewById<MaterialButton>(R.id.btnCases)
val btnUnits = binding.root.findViewById<MaterialButton>(R.id.btnUnits)
when (it) {
ScanningModes.CASE -> {
btnCases.setTextAppearance(R.style.Theme_MyApp_Toggle_Selected)
btnUnits.setTextAppearance(R.style.Theme_MyApp_Toggle_NotSelected)
}
ScanningModes.UNIT -> {
btnUnits.setTextAppearance(R.style.Theme_MyApp_Toggle_Selected)
btnCases.setTextAppearance(R.style.Theme_MyApp_Toggle_NotSelected)
}
}
The desired result is to flip the styles completely, but after a lot of trial and error (which got me to the above), what ends up happening is when Cases is selected (on initial load and by flipping back and forth between it and Units), I get this (desired result):
However, when I select Units, it does this:
This seems like it should be fairly easy to do, but I'm at a loss on how to accomplish this and hoping someone can assist.
Thanks in advance!

You don't need to switch style. You can just use one style with some selectors defining the android:state_checked state.
Something like:
<style name="App.Material3.Button.OutlinedButton" parent="Widget.Material3.Button.OutlinedButton">
<item name="backgroundTint">#color/app_m3_text_button_background_color_selector</item>
<item name="strokeColor">#color/app_button_outline_color_selector</item>
<item name="android:textColor">#color/app_text_button_foreground_color_selector</item>
</style>
with app_m3_text_button_background_color_selector:
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:color="#color/teal_200" android:alpha="0.12"
android:state_enabled="true" android:state_checked="true"/>
<item android:color="?attr/colorContainer"/>
</selector>
app_button_outline_color_selector:
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:alpha="0.12" android:color="?attr/colorOnSurface" android:state_enabled="false" />
<item android:alpha="0.12" android:color="#color/blu500_dark" android:state_enabled="true" android:state_checked="true"/>
<item android:color="?attr/colorOutline" />
</selector>
app_text_button_foreground_color_selector:
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<!-- Disabled -->
<item android:alpha="#dimen/material_emphasis_disabled" android:color="?attr/colorOnSurface" android:state_enabled="false" />
<!-- Uncheckable -->
<item android:color="?attr/colorOnContainer" android:state_checkable="false" />
<!-- Checked Buttons. -->
<item android:color="#color/blu500_dark" android:state_checked="true" />
<!-- Not-checked Buttons. -->
<item android:color="?attr/colorOnSurface" />
</selector>

After reviewing some additional details, I ended up solving the immediate problem by using setTextAppearance and setBackgroundColor since those are all I was really trying to change anyway. End result was this:
val btnCases = binding.root.findViewById<MaterialButton>(R.id.btnCases)
val btnUnits = binding.root.findViewById<MaterialButton>(R.id.btnUnits)
when (it) {
ScanningModes.CASE -> {
//change the text styles
btnCases.setTextAppearance(R.style.Theme_MyApp_Toggle_Selected)
btnUnits.setTextAppearance(R.style.Theme_MyApp_Toggle_NotSelected)
//change the background colors
btnCases.setBackgroundColor(ContextCompat.getColor(requireContext(), R.color.dark_blue))
btnUnits.setBackgroundColor(ContextCompat.getColor(requireContext(), R.color.white))
}
ScanningModes.UNIT -> {
btnUnits.setTextAppearance(R.style.Theme_MyApp_Toggle_Selected)
btnCases.setTextAppearance(R.style.Theme_MyApp_Toggle_NotSelected)
//change the background colors
btnUnits.setBackgroundColor(ContextCompat.getColor(requireContext(), R.color.dark_blue))
btnCases.setBackgroundColor(ContextCompat.getColor(requireContext(), R.color.white))
}
}
It's kind of amazing that changing a style directly isn't supported, but I'm sure there are reasons. Thankfully I didn't have to change more, or I'd have been stuck!

Related

It is possible to create a button by passing parameters in the XML android?

Is it possible to create a button by passing parameters in XML ?
doing it that way ?
<Button
button:typeParameter="primary"
button:size="md"/>
And after passing these 2 parameters the button is created as it should be, is it possible to do this?
I already have a button on which I created new attributes, now I want to know if it is possible to pass parameters and this button be called, without the need for the developer to have to code all these lines.
<customButton
android:fontFamily="#font/mondrian_family_font"
app:fontFamily="#font/mondrian_family_font"
android:id="#+id/mdnButtonPrimary"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textAllCaps="false"
android:text="Button primary"
android:paddingLeft="40dp"
android:paddingRight="40dp"
android:textColor="#color/color_neutral_lightest"
android:layout_marginTop="10dp"
android:textSize="#dimen/font_size_XXS"
mdnbutton:radius="#dimen/border_radius_pill"
mdnbutton:defaultColor="#color/color_brand_primary_medium"
mdnbutton:focusColor="#color/color_brand_primary_darkest"
style="?android:attr/borderlessButtonStyle"/>
It is possible that he will use these attributes after he installs my library.
Now I want that when the developer is going to create his layout, instead of creating a button from scratch, he just passes parameters in the XML and the button is rendered.
I suggest for you to make it as a style. But having it depend on two attributes is a bit hard, so I suggest to make a style for every combination of the two, so you have a style called primaryMd for example.
Your button would be like this:
<Button
style="#style/primaryMd" />
you can define this style in you res folder in styles.xml like this for example then:
<resources>
<style name="primaryMd" parent="#style/Widget.AppCompat.Button.Borderless">
<item name="android:fontFamily">#font/mondrian_family_font</item>
<item name="android:layout_width">wrap_content</item>
<item name="android:layout_height">wrap_content</item>
<item name="android:textAllCaps">false</item>
<item name="android:text">Button primary</item>
<item name="android:paddingLeft">40dp</item>
<item name="android:paddingRight">40dp</item>
<item name="android:textColor">#color/color_neutral_lightest</item>
<item name="android:layout_marginTop">10dp</item>
<item name="android:textSize">#dimen/font_size_XXS</item>
<item name="mdnbutton:radius">#dimen/border_radius_pill</item>
<item name="mdnbutton:defaultColor">#color/color_brand_primary_medium</item>
<item name="mdnbutton:focusColor">#color/color_brand_primary_darkest</item>
</style>
</resources>
I'm not entirely sure if it works with these custom mdnbutton attributes, but you could try it out maybe

Change Chip Widget style programmatically not working - Android

I'm doing a list with Chips. I want this chips can be selected, so, taking a look to https://material.io/develop/android/components/chip/ I see I can have a "Choice Chip".
As I need to create and add dynamically I have to configure with specific colors, color ripplem, ...
So what I have to configure it is:
val chip = Chip(context, null, R.style.CustomChipChoice)
chip.isClickable = true
chip.isCheckable = true
chip.isCheckedIconVisible=false
chip.height = ScreenUtils.dpToPx(40)
chip.chipCornerRadius = (ScreenUtils.dpToPx(20)).toFloat()
chip.chipStrokeWidth = (ScreenUtils.dpToPx(2)).toFloat()
chip.setTextAppearanceResource(R.style.ChipTextStyle)
return chip
What I try with R.style.CustomChipChoice is:
CustomChipChoice style
<style name="CustomChipChoice" parent="#style/Widget.MaterialComponents.Chip.Choice">
<item name="chipBackgroundColor">#color/background_color_chip_state_list</item>
<item name="chipStrokeColor">#color/background_color_chip_state_list</item>
<item name="rippleColor">#color/topic_social_pressed</item>
</style>
background_color_chip_state_list
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:color="#color/topic_social_selected" android:state_checked="true" />
<item android:color="#color/topic_social_pressed" android:state_pressed="true" />
<item android:color="#color/topic_unselected_background" />
</selector>
stroke_color_chip_state_list
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:color="#color/topic_social_pressed" android:state_checked="true"/>
<item android:color="#color/grey_material2" android:state_checked="false"/>
</selector>
As you can see, I make the chip, clickable and checkable (hiding the check icon I don't need).
But when I test it, the colors are not set. The chips just look as default colors (grey's scale)
Where can I apply or how, this custom style?
P.S:
I have done a fast test, to see if my CustomStyle was malformed/etc..
I added a view via xml and worked perfectly...
<android.support.design.chip.Chip
android:id="#+id/test"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
style="#style/CustomChipChoice"
android:checkable="true"
android:clickable="true"
app:checkedIconVisible="false"
android:text="Chip Test"/>
You can't use the constructor val chip = Chip(context, null, R.style.CustomChipChoice) because the 3rd parameter isn't the style but the attribute in the theme as R.attr.chipStyle.
The Chip hasn't a constructor with 4 parameters as other components because it extends AppCompatCheckbox which does not support a 4 parameter constructor.
However you can use something different.
1st option:
Just use a xml layout (single_chip_layout.xml) to define the single Chip with your favorite style:
<com.google.android.material.chip.Chip
xmlns:android="http://schemas.android.com/apk/res/android"
style="#style/CustomChipChoice"
...
/>
with
<style name="CustomChipChoice" parent="#style/Widget.MaterialComponents.Chip.Choice">
...
</style>
Then instead of val chip = Chip(context, null, R.style.CustomChipChoice) use:
val chip = layoutInflater.inflate(R.layout.single_chip_layout, chipGroup, false) as Chip
In java:
Chip chip =
(Chip) getLayoutInflater().inflate(R.layout.single_chip_layout, chipGroup, false);
2nd option:
Another option is to use the setChipDrawable method to override the ChipDrawable inside the Chip:
Chip chip = new Chip(this);
ChipDrawable chipDrawable = ChipDrawable.createFromAttributes(this,
null,
0,
R.style.Widget_MaterialComponents_Chip_Choice);
chip.setChipDrawable(chipDrawable);
In order to set the chip style in code you can try the following:
val chip = Chip(context)
val drawable = ChipDrawable.createFromAttributes(context, null, 0, R.style.Widget_MaterialComponents_Chip_Choice)
chip.setChipDrawable(drawable)
the CustomChipChoice is not a style it is just a reference to a style. therefore change R.style.CustomChipChoice to it : R.attr.CustomChipChoice
val newChip = Chip(context, null, R.attr.CustomChipChoice)
but before it you should add this CustomChipChoicein values.xml file in your project.
for this. if your project does not have the values.xml create it in values directory.
then add CustomChipChoice like this.
values.xml
<?xml version="1.0" encoding="utf-8"?>
<resources>
<attr name="CustomChipChoice" format="reference" />
</resources>
now in styles.xml add your style like this.
styles.xml
<style name="AppTheme" parent="Theme.MaterialComponents.Light.NoActionBar">
.
.
<item name="CustomChipChoice">#style/CustomChipChoiceStyle</item>
.
.
</style>
now that CustomChipChoice attr references to this style
and now you can create your custom style in styles.xml file.
styles.xml
<style name="CustomChipChoiceStyle" parent="#style/Widget.MaterialComponents.Chip.Action">
.
<item name="checkedIconVisible">false</item>
<item name="android:focusable">true</item>
<item name="android:clickable">true</item>
<item name="chipBackgroundColor">#color/colorWhite</item>
<item name="chipIcon">#drawable/ic_filter</item>
<item name="chipIconVisible">true</item>
<item name="textStartPadding">0dp</item>
<item name="textEndPadding">0dp</item>
.
.
<item name="android:textAppearance">#style/ChipTextStyleAppearance</item>
</style>
if you want to change text appearance of chip. here is ChipTextStyleAppearance. you can add it like this.
styles.xml
<style name="ChipTextStyleAppearance">
<item name="android:fontFamily">#font/main_font</item>
<item name="android:textSize">13dp</item>
<item name="android:textColor">#ffffff</item>
</style>
dont forget to add the AppTheme in androidManifest.xml on application or activity tags.
androidManifest.xml
<application
.
.
android:theme="#style/AppTheme">
<activity
.
.
android:theme="#style/AppTheme" />
There's another very simple approach to this.
styles.xml
<style name="Widget.MyApp.Chip" parent="Widget.MaterialComponents.Chip.Choice">
<item name="android:textAppearance">#style/TextAppearance.MyApp.Chip</item>
<item name="chipIconTint">?attr/colorPrimary</item>
</style>
theme.xml
<style name="Theme.MyApp.MyTheme" parent="Base.Theme.MyApp">
<item name="chipStyle">#style/Widget.MyApp.Chip</item>
</style>
With this, all chips in activities that have the theme Theme.MyApp.MyActivity applied to it will follow this custom style whether the chip has been added through xml or programmatically.
Kotlin
xml
<com.google.android.material.chip.ChipGroup
android:id="#+id/chipGroup"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:gravity="center_vertical"
android:orientation="horizontal">
</com.google.android.material.chip.ChipGroup>
Class
data class Parametro(
var idParametro: Long,
var nombreParametro: String? )
Main
listParametro.forEach { it->
val chip = Chip(context)
chip.id= it.idParametro.toInt()
chip.text= it.nombreParametro
chip.isClickable = true
chip.isCheckable = true
chip.setOnCheckedChangeListener { buttonView, isChecked ->
Log.i("checkedChipIds","${buttonView.id} $isChecked")
}
mBinding.chipGroup.addView(chip)
}
it works for me :)

Android change button style resource on click

I haven't been able to find quite what I am trying to do on SO, but I feel like it should be such a common interface need that there must be a straightforward way of accomplishing this that I'm missing.
In my style.xml I have two button styles, a standard "active" button and an "inactive" button.
<style name="ButtonStandard">
<item name="android:background">#color/colorGreen</item>
<item name="android:textColor">#android:color/white</item>
<item name="android:padding">#dimen/element_padding</item>
</style>
<style name="ButtonInactive">
<item name="android:background">#color/colorLight</item>
<item name="android:textColor">#android:color/black</item>
<item name="android:padding">#dimen/element_padding</item>
</style>
I am setting one button to ButtonStandard and the other to ButtonInactive. When I click the inactive button, I want to change it to use the ButtonStandard type and vice versa. I don't want to programmatically set the styles individually in case I decide to later change the button styles and it would be unreliable to have to change it in every location.
activeButton.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
activeButton.[somehowsetstyle](R.style.ButtonInactive);
inactiveButton.[somehowsetstyle](R.style.ButtonStandard);
}
});
How can I change between these styles when users click on buttons? The most important is to not have to set specific styles within the code which is just a last resort imho.
Thanks!
Solution Notes
Generally I followed the solution below but instead I created the selector as a drawable and used android:drawable instead because it seems the button background needs that, even if just specifying a color. I also used state_activated rather than enabled so that it is only changing the look of the button and doesn't prevent clicks.
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:state_activated="false"
android:drawable="#color/colorPrimaryDark" />
<item android:state_activated="true"
android:drawable="#color/colorGreen" />
<item android:drawable="#color/colorGreen" />
In XML
android:background="#drawable/selector_btn_bkg"
android:state_activated="false"
In Java
myButton.setActivated(true);
What you are looking for is ColorStateList
drawable/my_selector.xml
<selector xmlns:android="http://schemas.android.com/apk/res/android" >
<item android:color="#color/enabled_color"/>
<item android:color="#color/enabled_color"
android:state_enabled = "true"/>
<item android:color="#color/disbaled_color"
android:state_enabled = "false"/>
</selector>
my_view.xml
...
<Button
android:id="#+id/my_button"
android:enabled="false"
android:background="#drawable/my_selector"/>
Java code
onClick(View v){
myButton.setEnabled(true);
}

Why isn't my ImageButton style being applied to my ImageButton?

I have some styles set up to work on different API's (one for v21 and one for anything below that). I want to have a style for my ImageButton but it doesn't seem to be working out the way I expect.
The style for the v-21 is
<style name="BorderlessImageButton" parent="AppTheme.BorderlessImageButton">
<item name="android:background">?android:attr/selectableItemBackgroundBorderless</item>
<item name="android:tint">#color/secondary_text</item>
</style>
The style that will be used for all other API's below v-21 is
<style name="BorderlessImageButton" parent="AppTheme.BorderlessImageButton"/>
<style name="AppTheme.BorderlessImageButton" parent="#android:style/Widget.ImageButton">
<item name="android:tint">#color/secondary_text</item>
<item name="android:background">#color/borderless_button_background</item>
</style>
Here is my xml resource
<ImageButton
android:id="#+id/imageButton"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerVertical="true"
android:layout_toStartOf="#id/date_picker"
android:background="#null"
android:padding="10dp"
android:src="#drawable/ic_today_white_24dp"
android:theme="#style/BorderlessImageButton" />
If I run this on a device that has v-21 or v-22, the button doesn't visually react to my touch as I would expect using the ?android:attr/selectableItemBackgroundBorderless. Also, on any device below v-21, the button still doesn't react to the selector resource I have set up for it.
res/color/borderless_button_background.xml:
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:state_pressed="true" android:color="#color/accent" />
<item android:color="#color/transparent"/>
</selector>
Please help me get my button to react properly to touch based on which API the user has on their device.
Thanks
You need to apply your style with
style="#style/BorderlessImageButton"
The attribute android:theme is used for activities, see this.
UPDATE
Since Android 5.0, or using AppCompat 22.1.0+ (api 11+), you can also use a ThemeOverlay and use android:theme on single views. You can read about this technique here. Thanks to Ian for this.

Cannot change android Switch style when using Holo Everywhere

I am having trouble changing the style of a Switch view in my project and can't figure out what I am doing wrong or missing... My project uses holo everywhere.
I can't change the text, the thumb or the track. I've tried applying android:thumb and android:track drawables directly from the xml but nothing changes. Not even the android:textOff and android:textOn have any effect.
I found a great post on how to customise the android Switch view style and looked through all the code but couldn't find anything I was doing wrong:
http://custom-android-dn.blogspot.co.uk/2013/01/how-to-use-and-custom-switch-in-android.html
Really not sure what else to try.
Here is the xml code:
<Switch
android:id="#+id/home_availability_switch"
style="#style/MySwitch"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentLeft="true"
android:layout_centerVertical="true"
android:enabled="true"
android:focusable="true"
android:textOff="Free"
android:textOn="Busy" />
Here is the style:
<style name="MySwitch" parent="#style/Holo.Switch.Light">
<item name="android:thumb">#drawable/selector_availability_switch_thumb</item>
<item name="android:track">#drawable/selector_availability_switch_background</item>
</style>
Here is the thumb selector
(these are default system drawables. i have tried with different drawables):
<item android:drawable="#drawable/switch_thumb_pressed_holo_light" android:state_pressed="true"/>
<item android:drawable="#drawable/switch_thumb_activated_holo_light" android:state_checked="true"/>
<item android:drawable="#drawable/switch_thumb_disabled_holo_light" android:state_enabled="false"/>
<item android:drawable="#drawable/switch_thumb_holo_dark"/>
Here is the track selector
(these are default system drawables. i have tried with different drawables):
<item android:drawable="#drawable/switch_bg_disabled_holo_dark" android:state_enabled="false"/>
<item android:drawable="#drawable/switch_bg_focused_holo_dark" android:state_focused="true"/>
<item android:drawable="#drawable/switch_bg_holo_dark"/>
I also found this post How can I style an Android Switch? and follow the instructions but had no luck.
Any one else had a similar experience when using Holo Everywhere?
You need to define the switch like this. Need the whole package name in there for it to work.
<org.holoeverywhere.widget.Switch
android:id="#+id/lockSwitch"
style="?switchStyleOld"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="TestSwitch" />
Hope this helps.

Categories

Resources