1

I have a custom view LockButton which is just a FrameLayout that holds two specific child views. It holds an ImageButton as well as a type of custom view ProgressIndicator. This question centers around the behavior of LockButton and ImageButton. In particular I have added a custom state to the LockButton. The ImageButton has a StateListDrawable for it's source and a ColorStateList for its background.

The problem is when I change the state in LockButton, that change is not displayed in the ImageButton

State Changes

What am I doing wrong?

LockButton Class:

public class LockButton extends FrameLayout {

    private static final int[] STATE_LOCKED = {R.attr.state_locked};
    private boolean locked = true;

    View button;

    public LockButton(@NonNull Context context) {
        this(context, null);
    }

    public LockButton(@NonNull Context context, @Nullable AttributeSet attrs) {
        super(context, attrs);
        LayoutInflater.from(context).inflate(R.layout.lock_button, this);
        button = findViewById(R.id.lock_button_actual);

    }

    @Override
    public void setOnClickListener(@Nullable OnClickListener l) {
        button.setOnClickListener(l);
    }


    @Override
    public int[] onCreateDrawableState(int extraSpace) {

        final int[] drawableState;
        if(locked){
            drawableState = super.onCreateDrawableState(extraSpace + 1);
            mergeDrawableStates(drawableState, STATE_LOCKED);
        } else {
            drawableState = super.onCreateDrawableState(extraSpace);
        }
        return drawableState;
    }

    public void lock(boolean locked) {
        if(this.locked != locked) {
            this.locked = locked;
        }
        refreshDrawableState();
        button.refreshDrawableState();

    }

    public static class ProgressCircle extends View {...}

}

LockButton Layout:

<?xml version="1.0" encoding="utf-8"?>
<FrameLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    app:state_locked="true"
    android:layout_gravity="center"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    >

    <view class="com.example.LockButton$ProgressCircle"
        android:layout_width="68dp"
        android:layout_height="68dp"
        android:layout_gravity="center"
        />

    <ImageButton
        android:id="@+id/lock_button_actual"
        android:layout_width="@dimen/fab_size"
        android:layout_height="@dimen/fab_size"
        android:background="@drawable/button_lock_background"
        android:src="@drawable/icon_lock_status"
        android:tint="@color/white"
        android:elevation="6dp"
        android:layout_gravity="center"
        android:duplicateParentState="true"
        />
</FrameLayout>

Attributes XML:

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <declare-styleable name="lockState">
        <attr name="state_locked" format="boolean" />
    </declare-styleable>
</resources>

Drawable icon_lock_status.xml:

<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto">
    <item app:state_locked="false" android:drawable="@drawable/icon_lock_open" />
    <item app:state_locked="true" android:drawable="@drawable/icon_lock_closed"/>
</selector>

Drawable button_lock_background.xml:

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

Color button_lock_color.xml:

<?xml version="1.0" encoding="utf-8"?>
<selector
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto">
    <item app:state_locked="false" android:color="@color/grey_dark" />
    <item app:state_locked="true" android:color="@color/vibrant_red" />
</selector>

When I need to change the state, I simply call

lockButton.lock(true); // or false

on the LockButton


I have also tested to see if using default states instead of my custom one would work any better. This did not help. The ImageButton still does not respond to changes in the default states.

Setting a StateListDrawable as a background to the LockButton (FrameLayout) does work. So the state change is working properly on the parent, it is just not reflected in the child.

Stephen
  • 4,041
  • 22
  • 39
  • Does this work if you use one of the built in states? – natario Apr 16 '17 at 19:53
  • @natario Using the built in states did not have an effect. Info added at the bottom of my question. – Stephen Apr 16 '17 at 20:14
  • Also, why the `if (locked) {` ... branch? If I remember correctly, when a state is false, you should add the negative value (`-STATE_LOCKED`), not ignore it. – natario Apr 16 '17 at 20:28
  • From what reading I have done, it seems that you should include the state if it is true. If it is not however, it should simply be excluded. From looking into the android source, so far I don't see any special treatment of negative numbers. I also don't believe it is an issue of the state being created improperly as it is working for the FrameLayout/LockButton, just not the child ImageView. http://ptrprograms.blogspot.ca/2015/01/implementing-and-using-custom-drawable.html http://charlesharley.com/2012/programming/custom-drawable-states-in-android – Stephen Apr 16 '17 at 20:49
  • There is special treatment for negated values, see [this](http://stackoverflow.com/questions/5092649/android-how-to-update-the-selectorstatelistdrawable-programmatically) for instance, but maybe that’s not relevant to your case. I would try though :-) – natario Apr 16 '17 at 21:15
  • Ah, I see. You're correct then, there is use for negative states. However, it seems that has more to do with declaring the states programmatically, which I have already done in XML. I did test just in case, but it still did not help. – Stephen Apr 16 '17 at 23:21
  • I have tried the same and done some debugging and found out that the states being added in `onCreateDrawableState` method is not being added to the children. – Nataraj KR Mar 30 '21 at 07:42

0 Answers0