ListView 的多选模式

  昨天晚上熬粥,设定六个小时之后熬一个小时的,不知为什么后来变成一个小时之后熬了六个小时,今天早饭就只好改吃锅巴了。j_0064.gif

    在《ListView的单选模式》中,已经知道ListView有多选模式的,事实上我喜欢的作家也不只一个。ListView的多选模式起初写得比较简单,从中便发现了一些问题。

    先看一下布局文件,几乎和先前没怎么变化,只是改掉了ListView的模式而已。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
< LinearLayout  xmlns:android = "http://schemas.android.com/apk/res/android"
     xmlns:tools = "http://schemas.android.com/tools"
     android:layout_width = "match_parent"
     android:layout_height = "match_parent"
     android:orientation = "vertical"  >
 
     < Button
         android:layout_width = "match_parent"
         android:layout_height = "wrap_content"
         android:gravity = "center"
         android:onClick = "showSelectAuthors"
         android:text = "@string/select_authors"
         android:textSize = "25sp"  />
 
     < ListView
         android:id = "@+id/list"
         android:layout_width = "match_parent"
         android:layout_height = "match_parent"
         android:choiceMode = "multipleChoice"  />
 
</ LinearLayout >

    Activity的代码如下,没有用适配器来处理数据,简单使用了ArrayAdapter:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
package  com.example.choicelistviewtest2;
 
import  android.app.Activity;
import  android.os.Bundle;
import  android.view.View;
import  android.widget.ArrayAdapter;
import  android.widget.ListView;
import  android.widget.Toast;
 
public  class  RadioButtonListActivity  extends  Activity {
 
     private  ListView radioButtonList;
     private  String[] names =  new  String[] {  "芥川龙之介" "三岛由纪夫" "川端康成" "村上春树" ,
             "东野圭吾" "张爱玲" "金庸" "钱钟书" "老舍" "梁实秋" "亨利米勒" "海明威" "菲兹杰拉德" ,
             "凯鲁亚克" "杰克伦敦" "小仲马" "杜拉斯" "福楼拜" "雨果" "巴尔扎克" "莎士比亚" "劳伦斯" ,
             "毛姆" "柯南道尔" "笛福"  };
 
     @Override
     protected  void  onCreate(Bundle savedInstanceState) {
         super .onCreate(savedInstanceState);
         setContentView(R.layout.activity_main);
 
         radioButtonList = (ListView) findViewById(R.id.list);
         ArrayAdapter<String> adapter =  new  ArrayAdapter<String>( this ,
                 android.R.layout.simple_list_item_multiple_choice, names);
         radioButtonList.setAdapter(adapter);
     }
 
     public  void  showSelectAuthors(View v) {
         long [] authorsId = radioButtonList.getCheckItemIds();
 
         String name =  "" ;
         String message;
         if  (authorsId.length >  0 ) {
             // 用户至少选择了一位作家
             for  ( int  i =  0 ; i < authorsId.length; i++) {
                 name +=  ","  + names[( int ) authorsId[i]];
             }
             // 将第一个作家前面的“,”去掉
             message = name.substring( 1 );
         else  {
             message =  "请至少选择一位作家!" ;
         }
         Toast.makeText(RadioButtonListActivity. this , message, Toast.LENGTH_LONG)
                 .show();
     }
 
}

    上面的代码是成功的,程序运行也OK,本以为可以这样结束了,却发现一个问题:

wKiom1RwrICTEZBaAABW17_6ugI340.jpg

    从图上可以看出“getCheckItemIds()”这个方法是弃用的。事实上ListView的getCheckItemIds()方法所得到数据并不精确,据说在某些Android版本上测试发现,当我们选中ListView的一条Item,然后再次取消,getCheckItemIds()方法还是可以拿到取消的Item的id,即返回的数组中还保留该id。这是源码自己的Bug。

        虽然经过测试,我的手机上没发现这个问题(我的手机Android版本是4.3),但是我想这个方法还是避免使用吧。版本更新后Android推荐使用的是“getCheckedItemIds()”这个方法(注意方法名多加了“ed”),不过这个方法也不是那么好用——“Returns the set of checked items ids. The result is only valid if the choice mode has not been set toCHOICE_MODE_NONE and the adapter has stable IDs. (hasStableIds() == true)。”这个方法返回ListView中被选中Item的id集合。该方法使用有两个条件,第一是ListView的选择模式没有被设置为CHOICE_MODE_NONE(这一点我们满足,我们设置ListView的选择模式为CHOICE_MODE_MULTIPLE),第二是适配器有稳定的 ID(hasStableIds()==true)。这一点是不满足的,诸如ArrayAdapter、SimpleAdapter,不支持稳定的ID(可以通过adapter.hasStableIds()方法查看,返回值为false)。这就要求我们自己创建Adapter,从 hasStableIds()方法中返回true。

    我只好又自定义适配器试了一下这个方法,是成功的,布局文件没有改变,就不再贴了,主要是适配器,代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
package  com.example.choicelistviewtest3;
 
import  android.content.Context;
import  android.view.View;
import  android.view.ViewGroup;
import  android.widget.BaseAdapter;
 
public  class  RadioAdapter  extends  BaseAdapter {
 
     private  String[] authors;
     private  Context c;
 
     public  RadioAdapter(Context c, String[] authors) {
         super ();
         this .c = c;
         this .authors = authors;
     }
 
     @Override
     public  int  getCount() {
         return  authors.length;
     }
 
     @Override
     public  Object getItem( int  arg0) {
         return  null ;
     }
 
     @Override
     public  long  getItemId( int  arg0) {
         //返回每一条Item的Id
         return  arg0;
     }
 
     @Override
     public  boolean  hasStableIds() {
         //getCheckedItemIds()方法要求此处返回为真
         return  true ;
     }
     @Override
     public  View getView( int  arg0, View arg1, ViewGroup arg2) {
 
         ChoiceListItemView choiceListItemView =  new  ChoiceListItemView(c,  null );
         choiceListItemView.setName(authors[arg0]);
         return  choiceListItemView;
     }
 
}

    ChoiceListItemView类与《ListView的单选模式》中的大同小异,只是去掉了Button背景的设置,还原CheckBox原有的样子,因为现在ListView是多选模式。ChoiceListItemView代码与它的XML文件(Item的布局文件)如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
package  com.example.choicelistviewtest3;
 
import  android.content.Context;
import  android.util.AttributeSet;
import  android.view.LayoutInflater;
import  android.view.View;
import  android.widget.CheckBox;
import  android.widget.Checkable;
import  android.widget.LinearLayout;
import  android.widget.TextView;
 
public  class  ChoiceListItemView  extends  LinearLayout  implements  Checkable {
 
     private  TextView nameTxt;
     private  CheckBox selectBtn;
     public  ChoiceListItemView(Context context, AttributeSet attrs) {
         super (context, attrs);
 
         LayoutInflater inflater = LayoutInflater.from(context);
         View v = inflater.inflate(R.layout.item_list,  this true );
         nameTxt = (TextView) v.findViewById(R.id.author);
         selectBtn = (CheckBox) v.findViewById(R.id.radio);
     }
 
     public  void  setName(String text) {
         nameTxt.setText(text);
     }
 
     @Override
     public  boolean  isChecked() {
         return  selectBtn.isChecked();
     }
 
     @Override
     public  void  setChecked( boolean  checked) {
         selectBtn.setChecked(checked);
     }
 
     @Override
     public  void  toggle() {
         selectBtn.toggle();
     }
 
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
< RelativeLayout  xmlns:android = "http://schemas.android.com/apk/res/android"
     xmlns:tools = "http://schemas.android.com/tools"
     android:layout_width = "match_parent"
     android:layout_height = "wrap_content"
     android:background = "#fff"
     android:orientation = "horizontal"  >
 
     < TextView
         android:id = "@+id/author"
         android:layout_width = "wrap_content"
         android:layout_height = "wrap_content"
         android:layout_alignParentLeft = "true"
         android:layout_centerVertical = "true"
         android:padding = "10dp"
         android:textSize = "20sp"  />
    
     < CheckBox
         android:id = "@+id/radio"
         android:layout_width = "wrap_content"
         android:layout_height = "wrap_content"
         android:layout_alignParentRight = "true"
         android:layout_centerVertical = "true"
         android:layout_gravity = "center_vertical"
         android:clickable = "false"
         android:focusable = "false"
         android:focusableInTouchMode = "false"
         android:padding = "10dp"  />
 
</ RelativeLayout >

    这样,在主类中就可以使用“getCheckedItemIds()”这个方法了,代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
package  com.example.choicelistviewtest3;
 
import  android.app.Activity;
import  android.os.Bundle;
import  android.view.View;
 
import  android.widget.ListView;
import  android.widget.Toast;
 
public  class  RadioButtonListActivity  extends  Activity {
 
     private  ListView radioButtonList;
     private  RadioAdapter adapter;
     private  String[] authors =  new  String[] {  "芥川龙之介" "三岛由纪夫" "川端康成" "村上春树" ,
             "东野圭吾" "张爱玲" "金庸" "钱钟书" "老舍" "梁实秋" "亨利米勒" "海明威" "菲兹杰拉德" ,
             "凯鲁亚克" "杰克伦敦" "小仲马" "杜拉斯" "福楼拜" "雨果" "巴尔扎克" "莎士比亚" "劳伦斯" ,
             "毛姆" "柯南道尔" "笛福"  };
 
     @Override
     protected  void  onCreate(Bundle savedInstanceState) {
         super .onCreate(savedInstanceState);
         setContentView(R.layout.activity_radio_button_list);
 
         radioButtonList = (ListView) findViewById(R.id.list);
         adapter =  new  RadioAdapter( this , authors);
         radioButtonList.setAdapter(adapter);
     }
 
     public  void  showSelectAuthors(View v) {
         long [] authorsId = radioButtonList.getCheckedItemIds();
         String name =  "" ;
         String message;
         if  (authorsId.length >  0 ) {
             // 用户至少选择了一位作家
             for  ( int  i =  0 ; i < authorsId.length; i++) {
                 name +=  ","  + authors[( int ) authorsId[i]];
             }
             // 将第一个作家前面的“,”去掉
             message = name.substring( 1 );
         else  {
             message =  "请至少选择一位作家!" ;
         }
         Toast.makeText(RadioButtonListActivity. this , message, Toast.LENGTH_LONG)
                 .show();
     }
}

    它与choicelistviewtest2包中的RadioButtonListActivity 相比(也就是刚开始的那个RadioButtonListActivity 类),变化很小。显然,如果只是简单地显示一下作家的名字和复选框,而并不需要太多的要求,自定义Adapter实现拥有稳定的ID,这样做事实上是比较麻烦的。下面换一种简单的方法,还是使用ArrayAdapter,只是需要自己来写获取选中Item的ID的方法了,将choicelistviewtest2包中的RadioButtonListActivity增加一个方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
package  com.example.choicelistviewtest2;
 
import  android.app.Activity;
import  android.os.Bundle;
import  android.view.View;
import  android.widget.ArrayAdapter;
import  android.widget.ListView;
import  android.widget.Toast;
 
public  class  RadioButtonListActivity  extends  Activity {
 
     private  ListView radioButtonList;
     private  String[] names =  new  String[] {  "芥川龙之介" "三岛由纪夫" "川端康成" "村上春树" ,
             "东野圭吾" "张爱玲" "金庸" "钱钟书" "老舍" "梁实秋" "亨利米勒" "海明威" "菲兹杰拉德" ,
             "凯鲁亚克" "杰克伦敦" "小仲马" "杜拉斯" "福楼拜" "雨果" "巴尔扎克" "莎士比亚" "劳伦斯" ,
             "毛姆" "柯南道尔" "笛福"  };
 
     @Override
     protected  void  onCreate(Bundle savedInstanceState) {
         super .onCreate(savedInstanceState);
         setContentView(R.layout.activity_main);
 
         radioButtonList = (ListView) findViewById(R.id.list);
         ArrayAdapter<String> adapter =  new  ArrayAdapter<String>( this ,
                 android.R.layout.simple_list_item_multiple_choice, names);
         radioButtonList.setAdapter(adapter);
     }
 
     public  void  showSelectAuthors(View v) {
         // long[] authorsId = radioButtonList.getCheckItemIds();
         long [] authorsId = getListSelectededItemIds(radioButtonList);
         String name =  "" ;
         String message;
         if  (authorsId.length >  0 ) {
             // 用户至少选择了一位作家
             for  ( int  i =  0 ; i < authorsId.length; i++) {
                 name +=  ","  + names[( int ) authorsId[i]];
             }
             // 将第一个作家前面的“,”去掉
             message = name.substring( 1 );
         else  {
             message =  "请至少选择一位作家!" ;
         }
         Toast.makeText(RadioButtonListActivity. this , message, Toast.LENGTH_LONG)
                 .show();
     }
 
     // 避免使用getCheckItemIds()方法
     public  long [] getListSelectededItemIds(ListView listView) {
         
         long [] ids =  new  long [listView.getCount()]; //getCount()即获取到ListView所包含的item总个数
         //定义用户选中Item的总个数
         int  checkedTotal =  0 ;
         for  ( int  i =  0 ; i < listView.getCount(); i++) {
             //如果这个Item是被选中的
             if  (listView.isItemChecked(i)) {
                 ids[checkedTotal++] = i;
             }
         }
 
         if  (checkedTotal < listView.getCount()) {
             //定义选中的Item的ID数组
             final  long [] selectedIds =  new  long [checkedTotal];
             //数组复制 ids
             System.arraycopy(ids,  0 , selectedIds,  0 , checkedTotal);
             return  selectedIds;
         else  {
             //用户将所有的Item都选了
             return  ids;
         }
     }
 
}

      其中用到了System.arraycopy()这个方法,解释如下:

        public static void arraycopy(Object src, int srcPos, Object dest, int destPos, int length) src:源数组; srcPos:源数组要复制的起始位置; dest:目的数组; destPos:目的数组放置的起始位置; length:复制的长度。

    这就真正OK了,效果图:

wKioL1RwvlDwvTcbAAgnDZxsVYw757.jpg

wKiom1RwvdWiSrKFAAhUaS6Ff1A876.jpg

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值