Home > Software engineering >  Change Checkbox background on click?
Change Checkbox background on click?

Time:10-08

I have a Checkbox with custom images inside a selector...

and I am trying to achieve the following effect whnen the checkbox is clicked:

Gif for reference:

Tha background has to change while the checkbox is pressed and then goes back to normal when released.

The first two buttons are ImageButtons and the effect was easily achieved but with Checkbox I'm struggling. I guess it has to be something with the selector itself but I am not quite sure how to implement it. I tried adding a shape inside an item and a solid color but I couldn't make it work. Please point me in the right direction.

My checkbox code below (it uses two-way data binding):

 <CheckBox
    android:id="@ id/btn_addtofavs"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:layout_gravity="center"
    android:background="@drawable/inflated_selector_is_checked"
    android:button="@null"
    android:checked="@={inflatedItemViewModel.item.checked}"
    android:onClick="@{() -> inflatedItemViewModel.updateChecked(inflatedItemViewModel.item.id, inflatedItemViewModel.item.checked)}"
    tools:ignore="ContentDescription" />

My Checkbox selector:

<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">

    <item
        android:drawable="@drawable/ic_inflated_star_outline"
        android:state_checked="false"/>

    <item
        android:drawable="@drawable/ic_inflated_star_full"
        android:state_checked="true"/>

    <item
        android:drawable="@drawable/ic_inflated_star_outline"/>

</selector>

Other 2 buttons selector:

<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
    <item android:state_pressed="true">
        <shape>
            <solid android:color="@color/blue_200"/>
            <corners android:radius="@dimen/_8sdp"/>
        </shape>
    </item>

    <item android:state_pressed="false">
        <shape>
            <solid android:color="@color/blue_500"/>
            <corners android:radius="@dimen/_8sdp"/>
        </shape>
    </item>
</selector>

UPDATE 1: Suggested by Tenfour04

<com.google.android.material.button.MaterialButton
android:id="@ id/btn_addtofavs"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_gravity="center"
android:background="@drawable/bg_button"
android:checkable="true"
android:checked="@={inflatedItemViewModel.item.checked}"
android:onClick="@{() -> inflatedItemViewModel.updateChecked(inflatedItemViewModel.item.id, inflatedItemViewModel.item.checked)}"
app:icon="@drawable/inflated_selector_is_checked"
app:iconGravity="textStart"
app:iconPadding="0dp"
tools:ignore="ContentDescription" />

Got an error:

Cannot find a getter for <com.google.android.material.button.MaterialButton android:checked> that accepts parameter type 'boolean' 

i.e it's not recognized by the Compiler as e two-stage button. Do I miss something?

CodePudding user response:

Alright, after some hours of research I have come up with a working solution. It's a bit long, so bear with me. Here are the steps:

1. Define how the Star is going do look like when pressed and when released:

We are going to use layer lists.

star_full.xml

<?xml version="1.0" encoding="utf-8"?>
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">    
    <item>
        <shape>
            <solid android:color="@color/blue_500" />
            <corners android:radius="@dimen/_8sdp" />
            <padding
                android:bottom="@dimen/_12sdp"
                android:left="@dimen/_12sdp"
                android:right="@dimen/_12sdp"
                android:top="@dimen/_12sdp" />
        </shape>
    </item>

    <item android:drawable="@drawable/ic_inflated_star_full" />
</layer-list>

star_full_pressed.xml

<?xml version="1.0" encoding="utf-8"?>
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
    <item>
        <shape>
            <solid android:color="@color/blue_200" />
            <corners android:radius="@dimen/_8sdp" />
            <padding
                android:bottom="@dimen/_12sdp"
                android:left="@dimen/_12sdp"
                android:right="@dimen/_12sdp"
                android:top="@dimen/_12sdp" />
        </shape>
    </item>

    <item android:drawable="@drawable/ic_inflated_star_full" />
</layer-list>

star_outline.xml

<?xml version="1.0" encoding="utf-8"?>
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">    
    <item>
        <shape>
            <solid android:color="@color/blue_500" />
            <corners android:radius="@dimen/_8sdp" />
            <padding
                android:bottom="@dimen/_12sdp"
                android:left="@dimen/_12sdp"
                android:right="@dimen/_12sdp"
                android:top="@dimen/_12sdp" />
        </shape>
    </item>

    <item android:drawable="@drawable/ic_inflated_star_outline" />
</layer-list>

star_outline_pressed.xml

<?xml version="1.0" encoding="utf-8"?>
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
    <item>
        <shape>
            <solid android:color="@color/blue_200" />
            <corners android:radius="@dimen/_8sdp" />
            <padding
                android:bottom="@dimen/_12sdp"
                android:left="@dimen/_12sdp"
                android:right="@dimen/_12sdp"
                android:top="@dimen/_12sdp" />
        </shape>
    </item>

    <item android:drawable="@drawable/ic_inflated_star_outline" />
</layer-list>

2. Create two selectors - one for star_full and one for star_outline.

We are going to assign our layer lists to these two selectors and they are going to switch between state_pressed="true" and state_pressed="false" for each one of the two icons.

selector_star_full_is_pressed.xml

<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
    <item android:drawable="@drawable/star_full_pressed" android:state_pressed="true" />

    <item android:drawable="@drawable/star_full" android:state_pressed="false" />
</selector>

selector_star_outline_is_pressed.xml

<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
    <item android:drawable="@drawable/star_outline_pressed" android:state_pressed="true" />

    <item android:drawable="@drawable/star_outline" android:state_pressed="false" />
</selector>

3. Assign these two selectors to our Checkbox selector.

The checkbox selector will switch between android:state_checked="true" and android:state_checked="false".

selector_checkbox_is_checked.xml

<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">

    <item android:drawable="@drawable/selector_star_outline_is_pressed.xml" android:state_checked="false" />

    <item android:drawable="@drawable/selector_star_full_is_pressed.xml" android:state_checked="true" />

    <item android:drawable="@drawable/selector_star_outline_is_pressed.xml" />

</selector>

The final result should look like this:

CodePudding user response:

I don't use data binding, so I'm not sure this will work for you. But...

If you use a Material Components theme, Button is automatically converted to MaterialButton at runtime. MaterialButton is a lot more versatile than regular Button, and includes the features of a two-state button and an ImageButton all built in. This means it supports two-state behavior and has an android:checked property. Just make sure for the one that has two states that you set android:checkable="true".

The reason to use Button instead of CheckBox is that it will make it very easy to style it to look like your other buttons, because you can use the exact same styling for all of them. It would be very difficult to get the text, icon placement, and padding of a CheckBox to look just like the others.

To style it like what you have for all three of your buttons, you can set your icon using MaterialButton's app:icon. Set app:iconPadding="0dp" and app:iconGravity="textStart" so it will be centered in the button when there is no text.

I don't know exactly how you're doing it now, but I suggest instead of making a drawable selector for the background that you make a color selector (color state list), and set that to the app:backgroundTint property of the buttons. Then you won't lose the drop shadow and ripple effect. But perhaps you don't want it to look like that, in which case, ignore this paragraph.

  • Related