Java 在android上创建一个三态复选框

Java 在android上创建一个三态复选框,java,android,checkbox,Java,Android,Checkbox,我被困在一个大问题面前: 我想在android上设置三状态复选框。 它是列表视图上带有复选框的复选框。它应该允许用户在三种状态之间切换: 全部检查 未检查 杂项检查 并且可以选择在更改时保留misc状态 如果我是对的,我应该创建CompoundButton类的子类,并实现布尔mchecked的int-mstate-intead。 然后我应该重写事件侦听器、保存状态的函数以及状态getter和setter 我的问题基本上是如何实现这一点? 如何在可拖动状态之间切换?(我已经在xml中实现了中间

我被困在一个大问题面前: 我想在android上设置三状态复选框。 它是列表视图上带有复选框的复选框。它应该允许用户在三种状态之间切换:

  • 全部检查
  • 未检查
  • 杂项检查
并且可以选择在更改时保留misc状态

如果我是对的,我应该创建CompoundButton类的子类,并实现布尔mchecked的int-mstate-intead。 然后我应该重写事件侦听器、保存状态的函数以及状态getter和setter

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

以下是我启动的实现:

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;
    }
}
以下是stock 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;

/**
 * <p>
 * A button with two states, checked and unchecked. When the button is pressed
 * or clicked, the state changes automatically.
 * </p>
 *
 * <p><strong>XML attributes</strong></p>
 * <p>
 * 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}
 * </p>
 */
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;
    }

    /**
     * <p>Changes the checked state of this button.</p>
     *
     * @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<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.checked = isChecked();
        return ss;
    }

    @Override
    public void onRestoreInstanceState(Parcelable state) {
        SavedState ss = (SavedState) state;

        super.onRestoreInstanceState(ss.getSuperState());
        setChecked(ss.checked);
        requestLayout();
    }
}
/*
*版权所有(C)2007安卓开源项目
*
*根据Apache许可证2.0版(以下简称“许可证”)获得许可;
*除非遵守许可证,否则不得使用此文件。
*您可以通过以下方式获得许可证副本:
*
*      http://www.apache.org/licenses/LICENSE-2.0
*
*除非适用法律要求或书面同意,软件
*根据许可证进行的分发是按“原样”进行分发的,
*无任何明示或暗示的保证或条件。
*请参阅许可证以了解管理权限和权限的特定语言
*许可证下的限制。
*/
包android.widget;
导入com.android.internal.R;
导入android.content.Context;
导入android.content.res.TypedArray;
导入android.graphics.Canvas;
导入android.graphics.drawable.drawable;
导入android.os.packet;
导入android.os.Parcelable;
导入android.util.AttributeSet;
导入android.view.Gravity;
导入android.view.ViewDebug;
导入android.view.accessibility.AccessibilityEvent;
导入android.view.accessibility.AccessibilityNodeInfo;
/**
*
*有两种状态的按钮,选中和未选中。当按下按钮时
*或单击,状态将自动更改。
*

* *XML属性

* *请参阅{@linkandroid.R.styleable#CompoundButton *CompoundButton属性},{@link android.R.styleable#按钮 *属性},{@link android.R.styleable#TextView TextView Attributes},{@link *android.R.styleable#视图属性} *

*/ 公共抽象类CompoundButton扩展按钮实现可检查{ 私有布尔mChecked; 私人资源; 私有布尔型广播; 私人可提取mButtonDrawable; private OnCheckedChangeListener mOnCheckedChangeListener; private OnCheckedChangeListener mOnCheckedChangeWidgetListener; 私有静态最终int[]已检查\u状态\u集={ R.attr.state\u已检查 }; 公共复合按钮(上下文){ 这个(上下文,空); } 公共复合按钮(上下文、属性集属性){ 这(上下文,属性,0); } 公共复合按钮(上下文、属性集属性、int-defStyle){ 超级(上下文、属性、定义样式); Darray a型= context.actainStyledAttributes( attrs,com.android.internal.R.styleable.CompoundButton,defStyle,0); Drawable d=a.getDrawable(com.android.internal.R.styleable.CompoundButton\u按钮); 如果(d!=null){ 立根拔出(d); } 布尔选中=a .getBoolean(com.android.internal.R.styleable.CompoundButton_选中,false); setChecked(checked); a、 回收(); } 公共无效切换(){ setChecked(!mChecked); } @凌驾 公共布尔performClick(){ /* *XXX:这些都很小,需要一些周围的“扩展触摸区域”, *如果我们只覆盖 *performClick() */ /*单击后,切换状态*/ 切换(); 返回super.performClick(); } @ViewDebug.ExportedProperty 公共布尔值已检查(){ 返回mChecked; } /** *更改此按钮的选中状态

* *@param checked true可选中该按钮,false可取消选中该按钮 */ 公共void setChecked(布尔值已选中){ 如果(mChecked!=已检查){ mChecked=已检查; refreshDrawableState(); //如果从侦听器调用setChecked(),请避免无限递归 if(mBroadcasting){ 返回; } mBroadcasting=true; 如果(mOnCheckedChangeListener!=null){ mOnCheckedChangeListener.onCheckedChanged(这个,mChecked); } if(mOnCheckedChangeWidgetListener!=null){ mOnCheckedChangeWidgetListener.onCheckedChanged(这个,mChecked); } mBroadcasting=false; } } /** *注册一个回调,当此按钮处于选中状态时调用 *变化。 * *@param listener调用已检查状态更改的回调 */ public void setOnCheckedChangeListener(OnCheckedChangeListener侦听器){ mOnCheckedChangeListener=监听器; } /** *注册一个回调,当此按钮处于选中状态时调用 *更改。此回调仅用于内部目的。 * *@param listener调用已检查状态更改的回调 *@隐藏 */ void setOnCheckedChangeWidgetListener(OnCheckedChangeListener侦听器){ mOnCheckedChangeWidgetListener=监听器; } /** *当处于选中状态时要调用的回调的接口定义 *更改了复合按钮的名称。 */ OnCheckedChangeListener上的公共静态接口{ /** *当com的选中状态为
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content">

    <item android:state_checked="false"
          android:state_pressed="true"
          android:drawable="@drawable/btn_check_off_pressed" /> <!-- unchecked pressed -->

    <item android:state_checked="false"
          android:state_selected="true"
          android:drawable="@drawable/btn_check_off_selected" /> <!-- unchecked selected -->

    <item android:state_checked="true"
          android:state_pressed="false"
          android:state_focused="false"
          android:drawable="@drawable/btn_check_on" /> <!-- checked -->

    <item android:state_checked="true"
          android:state_pressed="true"
          android:drawable="@drawable/btn_check_on_pressed" /> <!-- checked pressed-->

    <item android:state_checked="true"
          android:state_selected="true"
          android:drawable="@drawable/btn_check_on_selected" /> <!-- checked selected-->

    <item android:state_middle="true"
          android:state_pressed="false"
          android:state_focused="false"
          android:drawable="@drawable/btn_check_middle" /> <!-- middle -->        

    <item android:state_middle="true"
          android:state_pressed="true"
          android:drawable="@drawable/btn_check_middle_pressed" /> <!-- middle pressed-->

    <item android:state_middle="true"
          android:state_selected="true"
          android:drawable="@drawable/btn_check_middle_selected"  /> <!-- middle selected--> 

    <item android:drawable="@drawable/btn_check_off" /> <!-- unchecked -->
</selector>
<?xml version="1.0" encoding="utf-8"?>
<!-- Copyright (C) 2008 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.
-->

<selector xmlns:android="http://schemas.android.com/apk/res/android">

    <!-- Enabled states -->

    <item android:state_checked="true" android:state_window_focused="false"
          android:state_enabled="true"
          android:drawable="@drawable/btn_check_on" />
    <item android:state_checked="false" android:state_window_focused="false"
          android:state_enabled="true"
          android:drawable="@drawable/btn_check_off" />

    <item android:state_checked="true" android:state_pressed="true"
          android:state_enabled="true"
          android:drawable="@drawable/btn_check_on_pressed" />
    <item android:state_checked="false" android:state_pressed="true"
          android:state_enabled="true"
          android:drawable="@drawable/btn_check_off_pressed" />

    <item android:state_checked="true" android:state_focused="true"
          android:state_enabled="true"
          android:drawable="@drawable/btn_check_on_selected" />
    <item android:state_checked="false" android:state_focused="true"
          android:state_enabled="true"
          android:drawable="@drawable/btn_check_off_selected" />

    <item android:state_checked="false"
          android:state_enabled="true"
          android:drawable="@drawable/btn_check_off" />
    <item android:state_checked="true"
          android:state_enabled="true"
          android:drawable="@drawable/btn_check_on" />


    <!-- Disabled states -->

    <item android:state_checked="true" android:state_window_focused="false"
          android:drawable="@drawable/btn_check_on_disable" />
    <item android:state_checked="false" android:state_window_focused="false"
          android:drawable="@drawable/btn_check_off_disable" />

    <item android:state_checked="true" android:state_focused="true"
          android:drawable="@drawable/btn_check_on_disable_focused" />
    <item android:state_checked="false" android:state_focused="true"
          android:drawable="@drawable/btn_check_off_disable_focused" />

    <item android:state_checked="false" android:drawable="@drawable/btn_check_off_disable" />
    <item android:state_checked="true" android:drawable="@drawable/btn_check_on_disable" />

</selector>
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) {
                    default:
                    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) {
            default:
            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();
    }
}