Android : Custom Android View Wont Display Changes

on Thursday, October 30, 2014


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