Scott Salyer
Scott Salyer

Reputation: 2475

Android MaterialButtonGroup Style Change

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

Cases Selected

However, when I select Units, it does this:

Units Selected

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!

Upvotes: 0

Views: 595

Answers (2)

Gabriele Mariotti
Gabriele Mariotti

Reputation: 364371

You don't need to switch style. You can just use one style with some selectors defining the android:state_checked state.

enter image description here

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>

Upvotes: 4

Scott Salyer
Scott Salyer

Reputation: 2475

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!

Upvotes: 0

Related Questions