I have a group of FrameLayout
which I want to be checkable/selectable,
That is, after a click I would like the FrameLayout
to display as
Adding the following code to a Checkable
class allows Selectors to work:
private static final int[] CheckedStateSet = {
android.R.attr.state_checked,
};
@Override
protected int[] onCreateDrawableState(int extraSpace) {
final int[] drawableState = super.onCreateDrawableState(extraSpace + 1);
if (isChecked()) {
mergeDrawableStates(drawableState, CheckedStateSet);
}
return drawableState;
}
@Override
public boolean performClick() {
toggle();
return super.performClick();
}
Try reordering tags in your selector_horizontal.xml
These are evaluated top to bottom. It seems that in your case either android:state_pressed="false" or android:state_pressed="true"
is applied and evaluation never reaches third line with android:state_checked="true"
Try moving first line to last:
<item android:drawable="@color/HighlightColor" android:state_pressed="true"/>
<item android:drawable="@color/selected_color" android:state_checked="true"/>
<item android:drawable="@android:color/transparent" android:state_pressed="false"/>
Try using android:state_activated
.
<item android:drawable="@color/HighlightColor" android:state_activated="true"/>
From the docs:
State value for StateListDrawable, set when a view or its parent has been "activated" meaning the user has currently marked it as being of interest.
In addition to Graeme's answer above do the following modification to toggle()
@Override
public void toggle() {
mChecked = !mChecked;
refreshDrawableState();
}
Here's a complete working example for a CheckableButton. It also works on Android 4.2.
public class CheckableButton extends Button implements Checkable {
private static final int[] CheckedStateSet = { android.R.attr.state_checked };
private boolean mChecked = false;
public CheckableButton(Context context) {
super(context);
}
public CheckableButton(Context context, AttributeSet attrs) {
super(context, attrs);
}
public CheckableButton(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
}
@Override
public boolean isChecked() {
return mChecked;
}
@Override
public void setChecked(boolean checked) {
mChecked = checked;
refreshDrawableState();
}
@Override
public void toggle() {
mChecked = !mChecked;
refreshDrawableState();
}
@Override
protected int[] onCreateDrawableState(int extraSpace) {
final int[] drawableState = super.onCreateDrawableState(extraSpace + 1);
if (isChecked()) {
mergeDrawableStates(drawableState, CheckedStateSet);
}
return drawableState;
}
}
Graeme's solution doesn't worked from me under Android 4.0.3 (and I suppose it won't work under 4.0 too). Instead you can change state_checked
to state_activated
:
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:drawable="@drawable/activated_image" android:state_pressed="false" android:state_activated="true" />
<item android:drawable="@drawable/not_activated_image" android:state_pressed="false" android:state_activated="false"/>
</selector>
and use setActivated
inside your setChecked
:
@Override
public void setChecked(boolean checked) {
mChecked = checked;
setActivated(checked);
}
and that is all. Implementing onCreateDrawableState
isn't required here.