• 支持多选的Spinner控件


    概述

    当我们要做单选功能的时候,我们会很自然的想到Spinner,它可以在一个集合中选择一个我们需要的值。但是有时候我们需要在一个集合中选择多个值,这个时候Spinner就不能满足需求。此时可以根据自己的需要来实现类似于Spinner效果的多选控件。

    效果图

    多选

    实现分析

    需要实现的效果是点击一个文本后弹出一个多选列表,在点击之后选择、取消选择,点击确定之后设置文本。这个文本框就用TextView,让它支持点击。点击之后弹出一个dialog就可以了,至于选择效果可以在ListView的Adapter里进行逻辑处理。下面开始具体步骤:

    1、首先需要用TextView来显示选择信息,在上面说明了,就继承TextView,在弹出对话框的时候需要一个标题,我们也传进来。然后就是要显示的数据集,因为考虑到可能显示的样式会和需要的值不一样,这里我们就自己定义一个类给它一个Name和Value属性。
    此外还有被选择的数据集,就用Set来存放。定义需要的属性,生成相应的get和set方法。

    public class MultiSpinner extends TextView implements View.OnClickListener,DialogInterface.OnClickListener{
    
        private ListView listView;
    
        private Context context;
    
        private String title;
    
        private List<SimpleSpinnerOption> dataList;
    
        private Adapter adapter;
    
        private Set<Object> checkedSet;
    
        private int selectCount=-1;
    
        private boolean isEmpty(){
            return dataList==null?true:dataList.isEmpty();
        }
    
        public String getTitle() {
            return title;
        }
    
        public void setTitle(String title) {
            this.title = title;
        }
    
        public void setCheckedSet(Set<Object> checkedSet) {
            this.checkedSet = checkedSet;
            showSelectedContent();
        }
    
        public List<SimpleSpinnerOption> getDataList() {
            return dataList;
        }
    
        public int getSelectCount() {
            return selectCount;
        }
    
        public void setSelectCount(int selectCount) {
            this.selectCount = selectCount;
        }
    
        public void setDataList(List<SimpleSpinnerOption> dataList) {
            this.dataList = dataList;
            if (adapter==null){
                adapter=new Adapter(dataList);
                this.listView.setAdapter(adapter);
            }else {
                adapter.setList(dataList);
                adapter.notifyDataSetChanged();
            }
        }
    
        public MultiSpinner(Context context) {
            super(context, null);
        }
    
        public MultiSpinner(Context context, AttributeSet attrs) {
            super(context, attrs);
            this.context=context;
            this.setOnClickListener(this);
            listView=new ListView(context);
            listView.setLayoutParams(new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,
                    ViewGroup.LayoutParams.MATCH_PARENT));
            adapter = new Adapter(null);
            this.listView.setAdapter(adapter);
        }
    
        public MultiSpinner(Context context, AttributeSet attrs, int defStyleAttr) {
            super(context, attrs, defStyleAttr);
        }
    }

    数据类型SimpleSpinnerOption的代码如下:

    public class SimpleSpinnerOption {
    
        private String name;
    
        private Object value;
    
        public SimpleSpinnerOption(){
            this.name="";
            this.value="";
        }
    
        public String getName() {
            return name;
        }
    
        public void setName(String name) {
            this.name = name;
        }
    
        public Object getValue() {
            return value;
        }
    
        public void setValue(Object value) {
            this.value = value;
        }
    }

    在上面的代码中,我们在初始化的时候为该控件本身设置了监听事件,由于此时还不知道数据源,所以为ListView设置了一个空的数据集。而在setDataList方法中拿到数据源并设置给listView中用于显示。为了显示出来,我们在控件本身的onClick事件中做显示工作,代码如下:

    @Override
        public void onClick(View view) {
            ViewGroup parent=(ViewGroup)listView.getParent();
            if(parent!=null){
                parent.removeView(listView);
            }
            if (dataList==null){
                Log.d("MultiSpinner","no data to show");
            }
            adapter.setCheckedSet(checkedSet);
            new AlertDialog.Builder(context)
                    .setTitle(title)
                    .setPositiveButton("确定",this)
                    .setNegativeButton("取消",this)
                    .setView(listView).show();
        }

    我们在onClick中弹出一个对话框让listview能够进行显示。并且设置了相应的点击事件。由于如果之前已经选择过了,再次点击控件,应该能把选择的效果还原出来。所以用adapter.setCheckedSet(checkedSet)来把已经选择的数据传入adapter中进行处理。

    接下来看一下Adapter适配器的代码:

    class Adapter extends BaseAdapter implements OnClickListener {
    
            private List<SimpleSpinnerOption> list;
    
            private Set<Object> checkedSet;
    
            public Adapter(List<SimpleSpinnerOption> list){
                this.list=list;
                checkedSet=new HashSet<Object>();
            }
    
            public void setList(List<SimpleSpinnerOption> list) {
                this.list = list;
            }
    
            public Set<Object> getCheckedSet(){
                return this.checkedSet;
            }
    
            public void setCheckedSet(Set<Object> checkedSet) {
                this.checkedSet=new HashSet<Object>();
                if(checkedSet!=null){
                    this.checkedSet.addAll(checkedSet);
                }
            }
    
            @Override
            public int getCount() {
                return list==null?0:list.size();
            }
    
            @Override
            public Object getItem(int position) {
                return list==null?null:list.get(position);
            }
    
            @Override
            public long getItemId(int position) {
                return position;
            }
    
            @Override
            public View getView(int position, View convertView, ViewGroup parent) {
                SimpleSpinnerOption mul=(SimpleSpinnerOption)this.getItem(position);
                Wrapper wrapper=null;
                if(convertView==null){
                    convertView = LayoutInflater.from(MultiSpinner.this.getContext()).inflate(R.layout.multi_spinner_item,null);
                    wrapper=new Wrapper();
                    wrapper.textView=(TextView)convertView.findViewById(R.id.textView);
                    wrapper.checkBox=(CheckBox)convertView.findViewById(R.id.checkBox);
                    wrapper.checkBox.setOnClickListener(this);
                    convertView.setTag(wrapper);
                }
                wrapper=(Wrapper)convertView.getTag();
                wrapper.textView.setText(mul.getName());
    
                if(checkedSet!=null){
                    if(checkedSet.contains(mul.getValue())){
                        wrapper.checkBox.setChecked(true);
                    }else{
                        wrapper.checkBox.setChecked(false);
                    }
                }
                wrapper.checkBox.setTag(position);
                return convertView;
            }
    
            @Override
            public void onClick(View v) {
                CheckBox checkBox=(CheckBox)v;
                Integer position=(Integer)checkBox.getTag();
                if(position==null){
                    return;
                }
                SimpleSpinnerOption op=(SimpleSpinnerOption)getItem(position);
                if(checkBox.isChecked()){
                    int maxCount= MultiSpinner.this.getSelectCount();
                    if(maxCount>-1&&checkedSet.size()>=maxCount){
                        checkBox.setChecked(false);
                        Toast.makeText(MultiSpinner.this.getContext(), String.format("最多只能选择 %s 个", selectCount), Toast.LENGTH_SHORT).show();
                        return;
                    }
                    checkedSet.add(op.getValue());
                }else{
                    checkedSet.remove(op.getValue());
                }
            }
    
            class Wrapper{
                public TextView textView;
                public CheckBox checkBox;
            }
        }

    在适配器的item布局里,用一个textview来显示文字,用checkbox来显示选中状态。在getView中判断是否已经选择修改checkbox的状态。同时在checkbox的点击事件中进行选择值checkedSet的修改。

    最后是我们点击确定取消按钮的逻辑:

    @Override
        public void onClick(DialogInterface dialogInterface, int i) {
            switch (i){
                case -1:
                    this.checkedSet=adapter.getCheckedSet();
                    showSelectedContent();
                    break;
            }
        }
    
    private void showSelectedContent(){
            StringBuilder sb=new StringBuilder();
            for(SimpleSpinnerOption option:getCheckedOptions()){
                sb.append(option.getName()).append(",");
            }
            if(sb.length()>0){
                sb.setLength(sb.length()-1);
            }
            setText(sb.toString());
        }

    代码很简单,就是拿到数据集之后进行一下展示,你也可以根据自己想要的展示方式进行修改。

    使用就在代码中找到控件同时设置一下数据集就ok了。在需要拿选择数据的时候调用multiSpinner.getCheckedOptions()做自己的处理。

    multiSpinner = (MultiSpinner) findViewById(R.id.mulSpinner);
            multiSpinner.setTitle("月份选择");
            ArrayList multiSpinnerList=new ArrayList();
            for(int i=0;i<12;i++){
                SimpleSpinnerOption option=new SimpleSpinnerOption();
                option.setName((i+1)+" 月");
                option.setValue(i+1);
                multiSpinnerList.add(option);
            }
            multiSpinner.setDataList(multiSpinnerList);

    到这里就实现了一个可以多选的类似于的spinner控件啦!
    源码下载请戳:自定义实现多选的Spinner控件

  • 相关阅读:
    HRBUST 1377 金明的预算【DP】
    POJ 1745 Divisibility【DP】
    HRBUST 1476 教主们毕业设计排版==算法导论上的思考题整齐打印
    HRBUST 1220 过河【DP+状态】
    HRBUST 1478 最长公共子序列的最小字典序
    HRBUST 1162 魔女【DP】
    HDU 1561The more, The Better【DP】
    HRBUST 1376 能量项链【DP】
    POJ 1934 Trip【最长公共子序列输出】
    上传图片代码总结
  • 原文地址:https://www.cnblogs.com/yangqiangyu/p/5143083.html
Copyright © 2020-2023  润新知