I needed a checkbox with three states (Unchecked, PartiallyChecked and Checked) much like you see in tree views when some children are selected for use in one of my android projects so I decided to create a custom View.
I did this by copying the code from CompoundButton.java (https://github.com/android/platform_frameworks_base/blob/master/core/java/android/widget/CompoundButton.java) and adding an extra state.
The mechanics of my new button work fine but I cant get it to display properly. The image shown doesnt change after I press it.
I think one issue is with the line Drawable d = a.getDrawable(R.styleable.ThreeStateRadioButton_button); in the constructor because d is null here. I am not sure how to fix this and even if i make it my button drawable directly it doesnt resolve my issue.
My code is below. Any help would be greatly appreciated.
Thanks,
ThreeStateButton.java
class ThreeStateRadioButton extends Button {
private int mState = 1;
final public static int UNCHECKED = 0;
final public static int HALF_CHECKED = 1;
final public static int CHECKED = 2;
private boolean mBroadcasting;
private int mButtonResource;
private Drawable mButtonDrawable;
private OnCheckedChangeListener mOnCheckedChangeListener;
private OnCheckedChangeListener mOnCheckedChangeWidgetListener;
private static final int[] CHECKED_STATE_SET = {
R.attr.state
};
public ThreeStateRadioButton(Context context) {
this(context,null);
}
public ThreeStateRadioButton(Context context, AttributeSet attrs){
this(context, attrs, 0);
}
public ThreeStateRadioButton(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
TypedArray a =
context.getTheme().obtainStyledAttributes(
attrs, R.styleable.ThreeStateRadioButton, defStyle, 0);
Drawable d = a.getDrawable(R.styleable.ThreeStateRadioButton_button);
if (d != null)
{
setButtonDrawable(d);
}
int state = a.getInt(R.styleable.ThreeStateRadioButton_state, 0);
setState(state);
a.recycle();
}
public void toggle(){
if(this.mState == this.UNCHECKED)
{setState(this.CHECKED);}
else if (this.mState == this.HALF_CHECKED)
{setState(this.CHECKED);}
else if (this.mState == this.CHECKED)
{setState(this.UNCHECKED);}
}
@Override
public boolean performClick(){
toggle();
return super.performClick();
}
public int getState() {
return mState;
}
public void setState(int state) {
if (mState != state) {
mState = state;
refreshDrawableState();
if(mBroadcasting)
{return;}
mBroadcasting = true;
if (mOnCheckedChangeListener != null) {
mOnCheckedChangeListener.onCheckedChanged(this, mState);
}
if (mOnCheckedChangeWidgetListener != null) {
mOnCheckedChangeWidgetListener.onCheckedChanged(this, mState);
}
mBroadcasting = false;
}
}
public void setOnCheckedChangeListener(OnCheckedChangeListener listener) {
mOnCheckedChangeListener = listener;
}
void setOnCheckedChangeWidgetListener(OnCheckedChangeListener listener) {
mOnCheckedChangeWidgetListener = listener;
}
public static interface OnCheckedChangeListener {
void onCheckedChanged(ThreeStateRadioButton buttonView, int State);
}
public void setButtonDrawable(int resId)
{
if (resId !=0 && resId == mButtonResource)
{
return;
}
mButtonResource = resId;
Drawable d = null;
if(mButtonResource != 0){
d = getResources().getDrawable(mButtonResource);
}
setButtonDrawable(d);
}
public void setButtonDrawable(Drawable d)
{
if (d!=null){
if(mButtonDrawable != null){
mButtonDrawable.setCallback(null);
unscheduleDrawable(mButtonDrawable);
}
d.setCallback(this);
d.setState(getDrawableState());
d.setVisible(getVisibility() ==VISIBLE, false);
mButtonDrawable = d;
mButtonDrawable.setState(null);
setMinHeight(mButtonDrawable.getIntrinsicHeight());
}
refreshDrawableState();
}
@Override
protected void onDraw(Canvas canvas){
super.onDraw(canvas);
final Drawable buttonDrawable = mButtonDrawable;
if(buttonDrawable != null){
final int verticalGravity = getGravity() & Gravity.VERTICAL_GRAVITY_MASK;
final int height = buttonDrawable.getIntrinsicHeight();
int y = 0;
switch (verticalGravity){
case Gravity.BOTTOM:
y = getHeight() - height;
break;
case Gravity.CENTER_VERTICAL:
y = (getHeight() - height) /2;
break;
}
buttonDrawable.setBounds(0,y,buttonDrawable.getIntrinsicWidth(),y+getHeight());
buttonDrawable.draw(canvas);
}
}
@Override
protected int[] onCreateDrawableState(int extraSpace){
final int[] drawableState = super.onCreateDrawableState(extraSpace + 1);
mergeDrawableStates(drawableState, CHECKED_STATE_SET);
return drawableState;
}
@Override
protected void drawableStateChanged()
{
super.drawableStateChanged();
if (mButtonDrawable != null){
int [] mDrawableState = getDrawableState();
mButtonDrawable.setState(mDrawableState);
invalidate();
}
}
@Override
protected boolean verifyDrawable(Drawable who){
return super.verifyDrawable(who) || who == mButtonDrawable;
}
static class SavedState extends BaseSavedState{
int state;
SavedState(Parcelable superState){
super(superState);
}
private SavedState(Parcel in){
super(in);
state = (Integer)in.readValue(null);
}
@Override
public void writeToParcel(Parcel out, int flags){
super.writeToParcel(out, flags);
out.writeValue(state);
}
@Override
public String toString() {
return "CompoundButton.SavedState{"
+ Integer.toHexString(System.identityHashCode(this))
+ " state=" + state + "}";
}
public static final Parcelable.Creator<SavedState> CREATOR
= new Parcelable.Creator<SavedState>() {
public SavedState createFromParcel(Parcel in) {
return new SavedState(in);
}
public SavedState[] newArray(int size) {
return new SavedState[size];
}
};
}
@Override
public Parcelable onSaveInstanceState() {
// Force our ancestor class to save its state
setFreezesText(true);
Parcelable superState = super.onSaveInstanceState();
SavedState ss = new SavedState(superState);
ss.state = getState();
return ss;
}
@Override
public void onRestoreInstanceState(Parcelable state) {
SavedState ss = (SavedState) state;
super.onRestoreInstanceState(ss.getSuperState());
setState(ss.state);
requestLayout();
}}
attrs.xml
<resources>
<declare-styleable name="ThreeStateRadioButton">
<attr name="state" format="enum">
<enum name="unchecked" value="0"/>
<enum name="half_checked" value="1"/>
<enum name="checked" value="2"/>
</attr>
<attr name="button" format="reference"/>
</declare-styleable>
</resources>
button.xml
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:custom="http://schemas.android.com/apk/res/my.app">
<item custom:state="unchecked" android:state_window_focused="false"
android:drawable="@drawable/btn_radio_off_holo_light" />
<item custom:state="checked" android:state_window_focused="false"
android:drawable="@drawable/btn_radio_on_holo_light" />
<item custom:state="half_checked" android:state_window_focused="false"
android:drawable="@drawable/btn_radio_on_disabled_holo_light" />
<item custom:state="checked" android:state_pressed="true"
android:drawable="@drawable/btn_radio_on_pressed_holo_light" />
<item custom:state="half_checked" android:state_pressed="true"
android:drawable="@drawable/btn_radio_on_pressed_holo_light" />
<item custom:state="unchecked" android:state_pressed="true"
android:drawable="@drawable/btn_radio_off_pressed_holo_light" />
<item custom:state="checked" android:state_focused="true"
android:drawable="@drawable/btn_radio_on_focused_holo_light" />
<item custom:state="checked" android:state_focused="true"
android:drawable="@drawable/btn_radio_on_disabled_focused_holo_light" />
<item custom:state="unchecked" android:state_focused="true"
android:drawable="@drawable/btn_radio_off_focused_holo_light" />
<item custom:state="unchecked" android:drawable="@drawable/btn_radio_on_holo_light" />
<item custom:state="checked" android:drawable="@drawable/btn_radio_off_holo_light" />
<item custom:state="half_checked" android:drawable="@drawable/btn_radio_on_disabled_focused_holo_light" />
</selector>
0 comments:
Post a Comment