编辑
多级选择框
本文访问次数:0

在做一个购物车页面的时候,经常遇到这种情况,购物车中的商品按店铺分开,每个店铺和商品都有一个选择框,还有一个全部选择的选择框。其中全部选择选择框的下级(子级)为所有店铺选择框,店铺选择框的下级为店铺下的商品选择框。这就是一个典型的多级(级联)选择框案例。必须满足以下所有条件才能使用本文的思路。

  • 每个级别只能有一个上级或者没有上级。
  • 每个级别有任意个下级。
  • 当前级别的选择框的选择状态由所有下级的选择状态(true或者false)做And运算。
  • 当前级别的选择框的选择状态由上级的选择状态决定,即上级为true时为true,上级为false时为false。

首先分析一下选择框改变状态的原因都包括哪些:

  • 点击
  • 初始化
    这个原因比较特殊,只有在listview或者gridview中包含checkbox时才会用到,一般这种情况下,checkbox的状态都由数据(即模型里的checked属性)决定,在getView函数的时候一般会掉用checkbox的setChecked方法,这个时候的原因就是初始化
  • 下级改变了状态
  • 上级改变了状态
    下级改变状态后需要检测所有的下级的选择状态,并做And运算。
    每种原因所需要进行的操作都不一样,以下为详细操作:
原因 操作
点击 通知上级检测它的状态,通知所有下级改变状态
初始化 不需要做任何操作
下级改变了状态 下级改变状态后需要检测所有的下级的选择状态,并做And运算。
上级改变了状态 直接将自己的状态改为父级的状态

需要注意的是对上下级操作时,需要传递原因

下面是CheckBox控件的详细代码


/**
 * Created by zongren on 16/5/19.
 */
public class TreeCheckbox extends AppCompatCheckBox {

    /* 为什么要改变自己的checked状态 */
    public enum WHY_CHANGE_STATUS {
        SELF_CLICKED,//onCheckedChanged事件来自于自己(即被点击,默认是这个原因)
        SELF_INIT,//onCheckedChanged事件来自于初始化(即代码调用setChecked函数)
        CHILD_CHANGED,//onCheckedChanged事件来自于子级改变了状态,导致需要检测自己的状态
        PARENT_CHANGED//onCheckedChanged事件来自于父级改变了状态,导致自己跟随改变(即跟父级保持一致)
    }
    private WHY_CHANGE_STATUS mWhyChangeStatus = WHY_CHANGE_STATUS.SELF_CLICKED;
    private int mParentCheckboxId;
    private TreeCheckbox mParentCheckbox;
    private List<TreeCheckbox> mChildrenCheckbox;

    public TreeCheckbox(Context context) {
        this(context, null);
    }

    public TreeCheckbox(Context context, AttributeSet attrs) {
        this(context, attrs, android.support.v7.appcompat.R.attr.checkboxStyle);
    }

    public TreeCheckbox(Context context, AttributeSet attrs, int defStyleAttr) {
        super(TintContextWrapper.wrap(context), attrs, defStyleAttr);
        TypedArray array = context.obtainStyledAttributes(attrs, R.styleable.TreeCheckbox);
        mParentCheckboxId = array.getInt(R.styleable.TreeCheckbox_parentCheckbox,0);
        array.recycle();
        mChildrenCheckbox = new ArrayList<>();
        setOnCheckedChangeListener(new OnCheckedChangeListener() {
            @Override
            public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
                switch (mWhyChangeStatus){
                    case SELF_CLICKED:
                        //自己被点击需要改变所有直接子级的状态,同时需要父级检测自己的状态
                        if(mParentCheckbox != null){
                            if(!Utils.isNull(mParentCheckbox.getChildrenCheckbox())){
                                boolean isChildrenAllChecked = true;
                                for (TreeCheckbox checkBox : mParentCheckbox.getChildrenCheckbox()) {
                                    isChildrenAllChecked = isChildrenAllChecked && checkBox.isChecked();
                                }
                                //父级改变状态的原因是子级改变了状态,所以是CHILD_CHANGED
                                mParentCheckbox.setChecked(isChildrenAllChecked,WHY_CHANGE_STATUS.CHILD_CHANGED);
                            }
                        }
                        if (!Utils.isNull(mChildrenCheckbox)) {
                            for (TreeCheckbox checkBox : mChildrenCheckbox) {
                                //所以子级更改状态的原因都是PARENT_CHANGED
                                checkBox.setChecked(isChecked, WHY_CHANGE_STATUS.PARENT_CHANGED);
                            }
                        }
                        break;
                    case SELF_INIT:
                        //通过代码初始化状态,不需要做任何处理
                        break;
                    case CHILD_CHANGED:
                        //因为子级改变了状态所以要继续向父级传递这个事件
                        if(mParentCheckbox != null){
                            if(!Utils.isNull(mParentCheckbox.getChildrenCheckbox())){
                                boolean isChildrenAllChecked = true;
                                for (TreeCheckbox checkBox : mParentCheckbox.getChildrenCheckbox()) {
                                    isChildrenAllChecked = isChildrenAllChecked && checkBox.isChecked();
                                }
                                //父级改变状态的原因是子级改变了状态,所以是CHILD_CHANGED
                                mParentCheckbox.setChecked(isChildrenAllChecked,WHY_CHANGE_STATUS.CHILD_CHANGED);
                            }
                        }
                        break;
                    case PARENT_CHANGED:
                        //因为父级改变了状态所以要继续向子级传递这个事件
                        if (!Utils.isNull(mChildrenCheckbox)) {
                            for (TreeCheckbox checkBox : mChildrenCheckbox) {
                                //所以子级更改状态的原因都是PARENT_CHANGED
                                checkBox.setChecked(isChecked, WHY_CHANGE_STATUS.PARENT_CHANGED);
                            }
                        }
                        break;
                }
                //无论什么原因导致自己的状态改变,最后都设置为默认原因,也就是SELF_CLICKED
                //mWhyChangeStatus = WHY_CHANGE_STATUS.SELF_CLICKED;
            }
        });
    }

    @Override
    public void onAttachedToWindow() {
        super.onAttachedToWindow();
        View parentCheckbox = getRootView().findViewById(mParentCheckboxId);
        if (parentCheckbox instanceof TreeCheckbox) {
            ((TreeCheckbox) parentCheckbox).addChildCheckbox(this);
            setParentCheckbox((TreeCheckbox) parentCheckbox);
        }
    }

    public void setChecked(boolean isChecked, WHY_CHANGE_STATUS reason) {
        mWhyChangeStatus = reason;
        setChecked(isChecked);
        //所以重置mWhyChangeStatus的操作放在这里,因为有可能没有监听器
        mWhyChangeStatus = WHY_CHANGE_STATUS.SELF_CLICKED;
    }

    public TreeCheckbox getParentCheckbox() {
        return mParentCheckbox;
    }

    public void setParentCheckbox(TreeCheckbox parentCheckbox) {
        mParentCheckbox = parentCheckbox;
    }

    public List<TreeCheckbox> getChildrenCheckbox() {
        return mChildrenCheckbox;
    }

    public void setChildrenCheckbox(List<TreeCheckbox> childrenCheckbox) {
        mChildrenCheckbox = childrenCheckbox;
    }

    public void addChildCheckbox(TreeCheckbox childCheckbox) {
        if (Utils.isNull(mChildrenCheckbox)) {
            mChildrenCheckbox = new ArrayList<>();
        }
        mChildrenCheckbox.add(childCheckbox);
    }

    public WHY_CHANGE_STATUS getWhyChangeStatus() {
        return mWhyChangeStatus;
    }

    public void setWhyChangeStatus(WHY_CHANGE_STATUS whyChangeStatus) {
        mWhyChangeStatus = whyChangeStatus;
    }
}

对于在ListView使用CheckBox的这种情况就需要做针对性的处理了,因为这个时候mParentCheckBox和mChildrenCheckBox都是不存在的。

没有任何评论