在android上创建一个三态checkbox

我遇到了一个大问题:我想在android上制作三个状态checkbox。 它是带有checkbox的ListView上的checkbox。 它应该允许用户在三种状态之间切换:

  • 全部检查
  • 没人检查过
  • misc检查

并且可选择在变化时保持misc状态。

如果我是对的,我应该创建CompoundButton类的子类并实现boolean mchecked的int mstate intead。 然后我应该覆盖事件监听器,保存状态的函数,以及状态getter和setter。

我的问题基本上是如何实现的? 如何在可绘制状态之间切换? (我已经在xml中实现了middle_state)以及如何正确实现事件处理程序?

这是我开始的实现:

public class TriStateCheckBox extends CompoundButton{ private int state; public TriStateCheckBox(Context context) { super(context); } public static interface onCheckChangedListener{ void onCheckChanged(TriStateCheckBox view, int state); } public void onCheckChanged(TriStateCheckBox view, int state){ this.state = state; } } 

这是股票CompoundButton的代码:

 /* * Copyright (C) 2007 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package android.widget; import com.android.internal.R; import android.content.Context; import android.content.res.TypedArray; import android.graphics.Canvas; import android.graphics.drawable.Drawable; import android.os.Parcel; import android.os.Parcelable; import android.util.AttributeSet; import android.view.Gravity; import android.view.ViewDebug; import android.view.accessibility.AccessibilityEvent; import android.view.accessibility.AccessibilityNodeInfo; /** * 

* A button with two states, checked and unchecked. When the button is pressed * or clicked, the state changes automatically. *

* *

XML attributes

*

* See {@link android.R.styleable#CompoundButton * CompoundButton Attributes}, {@link android.R.styleable#Button Button * Attributes}, {@link android.R.styleable#TextView TextView Attributes}, {@link * android.R.styleable#View View Attributes} *

*/ public abstract class CompoundButton extends Button implements Checkable { private boolean mChecked; private int mButtonResource; private boolean mBroadcasting; private Drawable mButtonDrawable; private OnCheckedChangeListener mOnCheckedChangeListener; private OnCheckedChangeListener mOnCheckedChangeWidgetListener; private static final int[] CHECKED_STATE_SET = { R.attr.state_checked }; public CompoundButton(Context context) { this(context, null); } public CompoundButton(Context context, AttributeSet attrs) { this(context, attrs, 0); } public CompoundButton(Context context, AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); TypedArray a = context.obtainStyledAttributes( attrs, com.android.internal.R.styleable.CompoundButton, defStyle, 0); Drawable d = a.getDrawable(com.android.internal.R.styleable.CompoundButton_button); if (d != null) { setButtonDrawable(d); } boolean checked = a .getBoolean(com.android.internal.R.styleable.CompoundButton_checked, false); setChecked(checked); a.recycle(); } public void toggle() { setChecked(!mChecked); } @Override public boolean performClick() { /* * XXX: These are tiny, need some surrounding 'expanded touch area', * which will need to be implemented in Button if we only override * performClick() */ /* When clicked, toggle the state */ toggle(); return super.performClick(); } @ViewDebug.ExportedProperty public boolean isChecked() { return mChecked; } /** *

Changes the checked state of this button.

* * @param checked true to check the button, false to uncheck it */ public void setChecked(boolean checked) { if (mChecked != checked) { mChecked = checked; refreshDrawableState(); // Avoid infinite recursions if setChecked() is called from a listener if (mBroadcasting) { return; } mBroadcasting = true; if (mOnCheckedChangeListener != null) { mOnCheckedChangeListener.onCheckedChanged(this, mChecked); } if (mOnCheckedChangeWidgetListener != null) { mOnCheckedChangeWidgetListener.onCheckedChanged(this, mChecked); } mBroadcasting = false; } } /** * Register a callback to be invoked when the checked state of this button * changes. * * @param listener the callback to call on checked state change */ public void setOnCheckedChangeListener(OnCheckedChangeListener listener) { mOnCheckedChangeListener = listener; } /** * Register a callback to be invoked when the checked state of this button * changes. This callback is used for internal purpose only. * * @param listener the callback to call on checked state change * @hide */ void setOnCheckedChangeWidgetListener(OnCheckedChangeListener listener) { mOnCheckedChangeWidgetListener = listener; } /** * Interface definition for a callback to be invoked when the checked state * of a compound button changed. */ public static interface OnCheckedChangeListener { /** * Called when the checked state of a compound button has changed. * * @param buttonView The compound button view whose state has changed. * @param isChecked The new checked state of buttonView. */ void onCheckedChanged(CompoundButton buttonView, boolean isChecked); } /** * Set the background to a given Drawable, identified by its resource id. * * @param resid the resource id of the drawable to use as the background */ 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); } /** * Set the background to a given Drawable * * @param d The Drawable to use as the background */ 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 public void onInitializeAccessibilityEvent(AccessibilityEvent event) { super.onInitializeAccessibilityEvent(event); event.setChecked(mChecked); } @Override public void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info) { super.onInitializeAccessibilityNodeInfo(info); info.setCheckable(true); info.setChecked(mChecked); } @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 + height); buttonDrawable.draw(canvas); } } @Override protected int[] onCreateDrawableState(int extraSpace) { final int[] drawableState = super.onCreateDrawableState(extraSpace + 1); if (isChecked()) { mergeDrawableStates(drawableState, CHECKED_STATE_SET); } return drawableState; } @Override protected void drawableStateChanged() { super.drawableStateChanged(); if (mButtonDrawable != null) { int[] myDrawableState = getDrawableState(); // Set the state of the Drawable mButtonDrawable.setState(myDrawableState); invalidate(); } } @Override protected boolean verifyDrawable(Drawable who) { return super.verifyDrawable(who) || who == mButtonDrawable; } @Override public void jumpDrawablesToCurrentState() { super.jumpDrawablesToCurrentState(); if (mButtonDrawable != null) mButtonDrawable.jumpToCurrentState(); } static class SavedState extends BaseSavedState { boolean checked; /** * Constructor called from {@link CompoundButton#onSaveInstanceState()} */ SavedState(Parcelable superState) { super(superState); } /** * Constructor called from {@link #CREATOR} */ private SavedState(Parcel in) { super(in); checked = (Boolean)in.readValue(null); } @Override public void writeToParcel(Parcel out, int flags) { super.writeToParcel(out, flags); out.writeValue(checked); } @Override public String toString() { return "CompoundButton.SavedState{" + Integer.toHexString(System.identityHashCode(this)) + " checked=" + checked + "}"; } public static final Parcelable.Creator CREATOR = new Parcelable.Creator() { 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.checked = isChecked(); return ss; } @Override public void onRestoreInstanceState(Parcelable state) { SavedState ss = (SavedState) state; super.onRestoreInstanceState(ss.getSuperState()); setChecked(ss.checked); requestLayout(); } }

这是我的xml state-liste实现(工作):

                      

这是checkbox的库存xml实现:

                     

老话题,但我给出了我的解决方案:

 public class CheckBoxTriStates extends CheckBox { static private final int UNKNOW = -1; static private final int UNCHECKED = 0; static private final int CHECKED = 1; private int state; public CheckBoxTriStates(Context context) { super(context); init(); } public CheckBoxTriStates(Context context, AttributeSet attrs) { super(context, attrs); init(); } public CheckBoxTriStates(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); init(); } private void init() { state = UNKNOW; updateBtn(); setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() { // checkbox status is changed from uncheck to checked. public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) { switch (state) { case UNKNOW: state = UNCHECKED; break; case UNCHECKED: state = CHECKED; break; case CHECKED: state = UNKNOW; break; } updateBtn(); } }); } private void updateBtn() { int btnDrawable = R.drawable.ic_checkbox_indeterminate_black; switch (state) { case UNKNOW: btnDrawable = R.drawable.ic_checkbox_indeterminate_black; break; case UNCHECKED: btnDrawable = R.drawable.ic_checkbox_unchecked_black; break; case CHECKED: btnDrawable = R.drawable.ic_checkbox_checked_black; break; } setButtonDrawable(btnDrawable); } public int getState() { return state; } public void setState(int state) { this.state = state; updateBtn(); } 

}

你可以在这里find按钮资源。 它完美无缺。

如果你真的坚持使用CheckBox,其中每个元素都有3个状态,那么下面的代码可能会有效但我还没有尝试过: – 创建一个扩展CheckBox类的类。 – 添加一个variables:int或bytetypes的“checkstate”。 该类应该有一个布尔“isChecked”variables。 – 重写onClick或onCheck方法,以便更改3个状态之间的checkstatevariables,而不是切换isCheckedvariables。 – 显示后,检查“checkstate”并做一些视觉效果以显示第3个状态。 – 最后,告诉我它是否有效。