4

I have the following custom CompoundButton:

public class CustomCompoundButton extends CompoundButton {

    public CustomCompoundButton(Context context) {
        this(context, null);
    }

    public CustomCompoundButton(Context context, AttributeSet attrSet) {
        super(context, attrSet);
        setup();
    }

    private void setup() {
        setBackgroundResource(R.drawable.button_selector);
        setGravity(Gravity.CENTER);
        setClickable(true);
    }
}

I set the width and height of the Button from code, after adding it to the layout:

button.getLayoutParams().width = myWidth;
button.getLayoutParams().height = myHeight;

button_selector.xml:

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

button_checked.xml:

<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
    android:shape="oval">
    <solid android:color="?colorAccent" />
</shape>

button_unchecked.xml:

<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
    android:shape="oval">
    <stroke
        android:width="2dp"
        android:color="?colorAccent" />
</shape>

This works as intended, the Button is an empty circle when not checked and a filled circle when checked.

The problem is i cannot add ripple effect on top of this behaviour.

I have tried to wrap the selector in ripple tags like this:

<?xml version="1.0" encoding="utf-8"?>
<ripple xmlns:android="http://schemas.android.com/apk/res/android"
    android:color="#ffffff">
    <selector>
        <item
            android:drawable="@drawable/button_checked"
            android:state_checked="true" />
        <item
            android:drawable="@drawable/button_unchecked"
            android:state_checked="false" />
    </selector>
</ripple>

There are multiple problems with this approach:

  • The background shapes are completely overriden by the ripple, they are not visible anymore (no matter if they're checked or not)

    The background shapes should remain the same, i'd just like to add ripple effect for when the button is clicked (either checked or unchecked)

  • The radius of the ripple effects is way too large, they overlap each other

    The ripple radius should be the same as the radius of my buttons.

I have no idea how to make this work, would really appreciate any advice.

Community
  • 1
  • 1
justanoob
  • 1,665
  • 6
  • 22
  • Have you tried adding the ripple selector on it's own but as a foreground attribute? – ditn Jun 28 '17 at 13:13
  • @ditn Yes i have. `setForeground()` is available for API 23+ only, so i created a layout XML that consists only a `CustomCompoundButton`. Set its `android:foreground` attribute to the ripple and inflated it from code. The `Button` appeared fine but the ripple didn't show at all. – justanoob Jun 28 '17 at 13:28

1 Answers1

1

Wrapping each shape in its own ripple instead of wrapping the whole selector in a ripple will have the desired effect.

See this comment for an example.

So, the button_unchecked.xml would look something like:

<?xml version="1.0" encoding="utf-8"?>
<ripple xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:color="#ffffff"
    tools:targetApi="lollipop">
    <item android:id="@android:id/mask">
        <shape android:shape="oval">
            <stroke
                android:width="2dp"
                android:color="?colorAccent" />
        </shape>
    </item>
    <item
        android:id="@android:id/background"
        android:drawable="?colorAccent" />
</ripple>