setTrackDrawable on android Switch resulting wrong image

484 views Asked by At

My android app has permanent WebSocket connection to Server. There's also a switch that provides the ability to turn off that connection. So now switch has 2 colors. If switch is on then WS is connected and switch is green. If switch is off then WS is disconnected and switch is red.

onlineoffline

So only user can change switch state on/off. But now lets assume that switch was on and internet went down. I want to show to user that state. So added 3rd state - yellow (which means smth is wrong)

online_but_broken

my_layout.xml

<Switch
      android:checked="true"
      android:gravity="left"
      android:text="Online"
      android:thumb="@drawable/switch_thumb"
      android:track="@drawable/switch_back"/>

switch_thumb.xml:

<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
    android:shape="oval">
    <solid
        android:color="@android:color/white"/>
    <stroke android:width="4dp" android:color="@android:color/transparent" />
    <size
        android:width="24dp"
        android:height="24dp"/>
</shape>

switch_back.xml:

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

switch_back_broken.xml

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

toogle_on.xml/toogle_off.xml/toggle_broken.xml:

ovals of different colors: ON is green, OFF is red, broken is orange.

<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android" android:shape="rectangle">
    <solid android:color="@android:color/transparent" />
    <corners android:radius="15dp"/>
    <solid
        android:color="@color/colorCheckGreen"/>
    <size
        android:width="40dp"
        android:height="24dp"/>
</shape>

Here's the implementation:

private Switch onlineSwitch;
int currentSwitchBack = R.drawable.switch_back;
@Subscribe
public void wsStatusChange(WsStatusChange event) {
    boolean isOnline = WsStatusChange.Status.OPENED.equals(event.getStatus());
    int newBackId = isOnline ? R.drawable.switch_back : R.drawable.switch_back_broken;
    if (currentSwitchBack != newBackId) {
        currentSwitchBack = newBackId;
        onlineSwitch.setTrackDrawable(getResources().getDrawable(newBackId));
    }
}

So the idea behind is to change background to switch_back_broken.xml when websocket goes down. Note that this background has same red color for offline mode, the only thing that's different is yellow online color. So thus both backgrounds cover all possible situations.

Now to the issue:

When switch is on and I change TrackDrawable from switch_back.xml (which is green) to switch_back_broken.xml (which is yellow) the UI displays RED background (notice that switch is in state ON), which should NEVER happen. Neither of my backgrounds contain red color for ON switch state. But if I double click on switch (meaning just retrigerring layout) colors go back to normal.

invalid state

I guess this behaviour comes from base state of switch, that's off by default. So switch component is painted with wrong layout guessing that it should be initially in state of off. Has anybody faced this issue?

Any suggestions how to come around? Any kind of help is appreciated!

Best regards,

1

There are 1 answers

0
deathangel908 On

I end up with ugly solution. Just manually retriggering switch:

private boolean block = false;
...
if (onlineSwitch.isChecked()) {
    block = true;
    onlineSwitch.setChecked(false);
    onlineSwitch.setChecked(true);
    block = false;
}

Note block is needed if you have onCheckedChanged listeners, so you just don't trigger them:

@Override
public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
    if (block) {
        return;     
    }
/// your code
}

I wonder if there's another way out. Maybe something like requestLayout or ...?