I want to create a CustomView
that displays an image. On click the view should change its state. There should be three states (off, set, notset) the view can represent. I want to do this with a selector in XML. It does not necesseraliy need to be a custom selector. I could reuse three states of the selector (it does not matter if the names of the state are different then).
Is there a good way to achieve this?
Just in case your issue has not resolved itself yet. I do the changing of states with a new implementation of the Android Button
.
The states are defined in .xml and set via a selector. Here the three states defined in the attrs.xml file:
<?xml version="1.0" encoding="utf-8"?>
<resources>
<declare-styleable name="states">
<attr name="state_on" format="boolean" />
<attr name="state_off" format="boolean" />
<attr name="state_notset" format="boolean" />
</declare-styleable>
</resources>
And the selector (statebutton_selector.xml) inside the drawables folder: (I assume that enabling a specific state automatically disables the other states - the drawables like "state_on" are just .png images representing the individual states)
<?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/com.example.statebuttontest">
<item
app:state_on="true"
app:state_off="false"
app:state_notset="false"
android:drawable="@drawable/state_on" />
<item
app:state_on="false"
app:state_off="true"
app:state_notset="false"
android:drawable="@drawable/state_off" />
<item
app:state_on="false"
app:state_off="false"
app:state_notset="true"
android:drawable="@drawable/state_notset" />
</selector>
Also, be aware to reference your correct package name in the selector xml file, as stated in your Manifest file:
xmlns:app="http://schemas.android.com/apk/res/com.example.statebuttontest"
And finally, the StateButton
class that extends Button
. With a simple OnClickListener
the state is changed. I also implemented an OnStateChangedListener
that for example can be implemented by an Activity that contains the Button and will be called whenever the state changes.
The changing of the state itself is done inside the onCreateDrawableState(...)
method that is called automatically every time the Button is clicked. "extraspace + 1" means, that there will be one additional state inside the drawableStates array.
public class StateButton extends Button implements OnClickListener {
private static final int[] mStates = { R.attr.state_notset, R.attr.state_on, R.attr.state_off };
private int mStateIndex = 0; // first state is "notset"
private OnStateChangedListener mListener;
public StateButton(Context context, AttributeSet attrs) {
super(context, attrs);
setOnClickListener(this);
}
@Override
public void onClick(View v) {
changeState();
}
public void changeState() {
mStateIndex = (mStateIndex+1) % mStates.length;
// notify listener
if(mListener != null) mListener.onStateChanged(mStates[mStateIndex]);
}
@Override
protected int[] onCreateDrawableState(int extraSpace) {
final int[] drawableState = super.onCreateDrawableState(extraSpace+1);
int [] state = { mStates[mStateIndex] };
mergeDrawableStates(drawableState, state);
return drawableState;
}
public void setOnStateChangedListener(OnStateChangedListener l) {
this.mListener = l;
}
}
Last but not least, set the selector as the background of your Button
:
<com.example.statebuttontest.StateButton
android:id="@+id/stateButton1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerHorizontal="true"
android:layout_centerVertical="true"
android:background="@drawable/statebutton_selector"
android:text="" />
An example of the Activity
(with Listener):
public class MainActivity extends Activity implements OnStateChangedListener {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
StateButton s = (StateButton) findViewById(R.id.stateButton1);
s.setOnStateChangedListener(this);
}
@Override
public void onStateChanged(int state) {
Log.i("Main", "State changed to: " + getResources().getResourceEntryName(state));
}
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With