概述

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

效果图

实现分析

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

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

此外还有被选择的数据集,就用Set来存放。定义需要的属性,生成相应的get和set方法。

  1. public class MultiSpinner extends TextView implements View.OnClickListener,DialogInterface.OnClickListener{
  2. private ListView listView;
  3. private Context context;
  4. private String title;
  5. private List<SimpleSpinnerOption> dataList;
  6. private Adapter adapter;
  7. private Set<Object> checkedSet;
  8. private int selectCount=-1;
  9. private boolean isEmpty(){
  10. return dataList==null?true:dataList.isEmpty();
  11. }
  12. public String getTitle() {
  13. return title;
  14. }
  15. public void setTitle(String title) {
  16. this.title = title;
  17. }
  18. public void setCheckedSet(Set<Object> checkedSet) {
  19. this.checkedSet = checkedSet;
  20. showSelectedContent();
  21. }
  22. public List<SimpleSpinnerOption> getDataList() {
  23. return dataList;
  24. }
  25. public int getSelectCount() {
  26. return selectCount;
  27. }
  28. public void setSelectCount(int selectCount) {
  29. this.selectCount = selectCount;
  30. }
  31. public void setDataList(List<SimpleSpinnerOption> dataList) {
  32. this.dataList = dataList;
  33. if (adapter==null){
  34. adapter=new Adapter(dataList);
  35. this.listView.setAdapter(adapter);
  36. }else {
  37. adapter.setList(dataList);
  38. adapter.notifyDataSetChanged();
  39. }
  40. }
  41. public MultiSpinner(Context context) {
  42. super(context, null);
  43. }
  44. public MultiSpinner(Context context, AttributeSet attrs) {
  45. super(context, attrs);
  46. this.context=context;
  47. this.setOnClickListener(this);
  48. listView=new ListView(context);
  49. listView.setLayoutParams(new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,
  50. ViewGroup.LayoutParams.MATCH_PARENT));
  51. adapter = new Adapter(null);
  52. this.listView.setAdapter(adapter);
  53. }
  54. public MultiSpinner(Context context, AttributeSet attrs, int defStyleAttr) {
  55. super(context, attrs, defStyleAttr);
  56. }

数据类型SimpleSpinnerOption的代码如下:

  1. public class SimpleSpinnerOption {
  2. private String name;
  3. private Object value;
  4. public SimpleSpinnerOption(){
  5. this.name="";
  6. this.value="";
  7. }
  8. public String getName() {
  9. return name;
  10. }
  11. public void setName(String name) {
  12. this.name = name;
  13. }
  14. public Object getValue() {
  15. return value;
  16. }
  17. public void setValue(Object value) {
  18. this.value = value;
  19. }
  20. }

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

  1. @Override
  2. public void onClick(View view) {
  3. ViewGroup parent=(ViewGroup)listView.getParent();
  4. if(parent!=null){
  5. parent.removeView(listView);
  6. }
  7. if (dataList==null){
  8. Log.d("MultiSpinner","no data to show");
  9. }
  10. adapter.setCheckedSet(checkedSet);
  11. new AlertDialog.Builder(context)
  12. .setTitle(title)
  13. .setPositiveButton("确定",this)
  14. .setNegativeButton("取消",this)
  15. .setView(listView).show();
  16. }

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

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

  1. class Adapter extends BaseAdapter implements OnClickListener {
  2. private List<SimpleSpinnerOption> list;
  3. private Set<Object> checkedSet;
  4. public Adapter(List<SimpleSpinnerOption> list){
  5. this.list=list;
  6. checkedSet=new HashSet<Object>();
  7. }
  8. public void setList(List<SimpleSpinnerOption> list) {
  9. this.list = list;
  10. }
  11. public Set<Object> getCheckedSet(){
  12. return this.checkedSet;
  13. }
  14. public void setCheckedSet(Set<Object> checkedSet) {
  15. this.checkedSet=new HashSet<Object>();
  16. if(checkedSet!=null){
  17. this.checkedSet.addAll(checkedSet);
  18. }
  19. }
  20. @Override
  21. public int getCount() {
  22. return list==null?0:list.size();
  23. }
  24. @Override
  25. public Object getItem(int position) {
  26. return list==null?null:list.get(position);
  27. }
  28. @Override
  29. public long getItemId(int position) {
  30. return position;
  31. }
  32. @Override
  33. public View getView(int position, View convertView, ViewGroup parent) {
  34. SimpleSpinnerOption mul=(SimpleSpinnerOption)this.getItem(position);
  35. Wrapper wrapper=null;
  36. if(convertView==null){
  37. convertView = LayoutInflater.from(MultiSpinner.this.getContext()).inflate(R.layout.multi_spinner_item,null);
  38. wrapper=new Wrapper();
  39. wrapper.textView=(TextView)convertView.findViewById(R.id.textView);
  40. wrapper.checkBox=(CheckBox)convertView.findViewById(R.id.checkBox);
  41. wrapper.checkBox.setOnClickListener(this);
  42. convertView.setTag(wrapper);
  43. }
  44. wrapper=(Wrapper)convertView.getTag();
  45. wrapper.textView.setText(mul.getName());
  46. if(checkedSet!=null){
  47. if(checkedSet.contains(mul.getValue())){
  48. wrapper.checkBox.setChecked(true);
  49. }else{
  50. wrapper.checkBox.setChecked(false);
  51. }
  52. }
  53. wrapper.checkBox.setTag(position);
  54. return convertView;
  55. }
  56. @Override
  57. public void onClick(View v) {
  58. CheckBox checkBox=(CheckBox)v;
  59. Integer position=(Integer)checkBox.getTag();
  60. if(position==null){
  61. return;
  62. }
  63. SimpleSpinnerOption op=(SimpleSpinnerOption)getItem(position);
  64. if(checkBox.isChecked()){
  65. int maxCount= MultiSpinner.this.getSelectCount();
  66. if(maxCount>-1&&checkedSet.size()>=maxCount){
  67. checkBox.setChecked(false);
  68. Toast.makeText(MultiSpinner.this.getContext(), String.format("最多只能选择 %s 个", selectCount), Toast.LENGTH_SHORT).show();
  69. return;
  70. }
  71. checkedSet.add(op.getValue());
  72. }else{
  73. checkedSet.remove(op.getValue());
  74. }
  75. }
  76. class Wrapper{
  77. public TextView textView;
  78. public CheckBox checkBox;
  79. }
  80. }

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

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

  1. @Override
  2. public void onClick(DialogInterface dialogInterface, int i) {
  3. switch (i){
  4. case -1:
  5. this.checkedSet=adapter.getCheckedSet();
  6. showSelectedContent();
  7. break;
  8. }
  9. }
  10. private void showSelectedContent(){
  11. StringBuilder sb=new StringBuilder();
  12. for(SimpleSpinnerOption option:getCheckedOptions()){
  13. sb.append(option.getName()).append(",");
  14. }
  15. if(sb.length()>0){
  16. sb.setLength(sb.length()-1);
  17. }
  18. setText(sb.toString());
  19. }

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

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

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

到这里就实现了一个可以多选的类似于的spinner控件啦!

源码下载请戳:自定义实现多选的Spinner控件

支持多选的Spinner控件的更多相关文章

  1. CYQ.Data 支持WPF相关的数据控件绑定(2013-08-09)

    事件的结果 经过多天的思考及忙碌的开发及测试,CYQ.Data 终于在UI上全面支持WPF,至此,CYQ.Data 已经可以方便支持wpf的开发,同时,框架仍保留最低.net framework2.0 ...

  2. Spinner控件

    首先在XML文件中声明一个Spinner控件: <Spinner android:id="@+id/spinnerId" android:layout_width=" ...

  3. Android spinner控件

    spinner控件是Android中下拉控件,现在介绍它两种用法.第一种,从资源文件中获取下拉值:第二种,从代码中获取下拉值. 第一种,首先要在资源文件中把值写好: <?xml version= ...

  4. jasonTree多选多级树控件

    jasonTree1.0 jasonTree多选多级树控件(名字是自己取),用于友好的展示树形结构的数据,并可以多选,传统的做法是在一个select的下拉框中显示一个可折叠的树结构,公司的需求人员这种 ...

  5. ProgressBar、RatingBar和Spinner控件

    1.ProgressBar.SeekBar与RatingBar控件 ProgressBar控件,也就是我们通常的进度条控件,可以显示加载的进度等.SeekBar控件,滑块控件,可以根据用户的需要动态为 ...

  6. 大约Android PopupWindow有用Spinner控件点击APP Crash案例整理!

    场景异常,如下面: android.view.WindowManager$BadTokenException: Unable to add window -- token android.view.V ...

  7. CYQ.Data 支持WPF相关的数据控件绑定.Net获取iis版本

    CYQ.Data 支持WPF相关的数据控件绑定(2013-08-09) 事件的结果 经过多天的思考及忙碌的开发及测试,CYQ.Data 终于在UI上全面支持WPF,至此,CYQ.Data 已经可以方便 ...

  8. 支持Angular 2的表格控件

    前端框架一直这最近几年特别火的一个话题,尤其是Angular 2拥有众多的粉丝.在2016年9月份Angular 2正式发布之后,大量的粉丝的开始投入到了Angular 2的怀抱.当然这其中也包括我. ...

  9. android 学习 Spinner控件的使用

    今晚看了下spinner控件的使用,结合博客大神的教程,一个小demo 一,SpinnerActivity private Spinner spinner; private ArrayAdapter& ...

随机推荐

  1. Unity 脚本系统

    什么是脚本?脚本是一个 MonoBehavior, 继承关系是 MonoBehavior -> Behavior -> Component -> Object GameObject ...

  2. Adroid学习系列-入门(1)

    1.  安装 安装Eclipse,安装Adroid插件.安装Adroid SDK. 2.  项目目录说明 2.1.       建立Adroid项目 与一般的Java项目一样 )src文件夹是项目的所 ...

  3. 【分布式】RPC初探

    事先声明:本文代码参考自Dubbo作者的博客. RPC(Remote Procedure Call)远程过程调用,是分布式系统当中必不可少的一个玩意.比如说在单机系统当中,我想调用某个方法,直接调就可 ...

  4. DirectWrite文字排版——字符串去尾

    DirectWrite是 DirectX 家族中专门用来做文本处理的部分,主要配合Direct2D进行渲染工作. 一.字符串去尾介绍 在文字渲染中,不免会遇到字符串去尾的需求.字符串去尾指的是:当字符 ...

  5. SQL Server中的事务日志管理(6/9):大容量日志恢复模式里的日志管理

    当一切正常时,没有必要特别留意什么是事务日志,它是如何工作的.你只要确保每个数据库都有正确的备份.当出现问题时,事务日志的理解对于采取修正操作是重要的,尤其在需要紧急恢复数据库到指定点时.这系列文章会 ...

  6. SerializationUtility

    public static T LoadFromXml<T>(string fileName) { FileStream fs = null; try { XmlSerializer se ...

  7. nodejs+express+jade+mongodb给我baby做个小相册(2)-留言板

    上一篇简单的实现了下照片的展现跟浏览功能,这一篇我将给这个程序添加一个留言的功能.那么留言的话肯定要涉及到数据持久了,其实对于这个小功能的话,用个xml就可以,不过为了看起来更加高大上,我决定使用mo ...

  8. thread_CyclicBarrier回环栅栏

    CyclicBarrier回环栅栏,字面意思是可循环使用(Cyclic)的屏障(Barrier).通过它可以实现让一组线程等待至某个状态之后再全部同时执行. 它要做的事情是,让一组线程到达一个屏障(也 ...

  9. .net C# 对虚拟目录IIS的操作

    一.查看虚拟目录是否存在 private bool IsExitesVirtualDir(string virtualdirname) {    bool exited =false;    Dire ...

  10. 使用SQLite数据库和Access数据库的一些经验总结

    在我的<Winform开发框架>中,可使用多种数据库作为程序的数据源,除了常规的Oracle数据库.SqlServer.MySql数据库,其中还包括了SQLite数据库.Access数据库 ...