ListView 的多选模式

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

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

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

<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:

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,本以为可以这样结束了,却发现一个问题:

从图上可以看出“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 to CHOICE_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。

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

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的布局文件)如下:

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();
	}

}
<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()”这个方法了,代码如下:

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增加一个方法:

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了,效果图:

时间: 2024-08-21 16:43:16

ListView 的多选模式的相关文章

ListView 实现多选/单选

ListView自身带了单选.多选模式,可通过listview.setChoiceMode来设置: listview.setChoiceMode(ListView.CHOICE_MODE_MULTIPLE);//开启多选模式 listview.setChoiceMode(ListView.CHOICE_MODE_SINGLE);//开启单选模式 listview.setChoiceMode(ListView.CHOICE_MODE_NONE);//默认模式 listview.setChoiceM

Android ListView条目全选功能,不用checkbox实现!

大家好,翻了翻以前的笔记,发现了一个我特别标记的功能,那就是ListView全选功能,顿时想起了我那个时候苦逼的生涯,由于我大学机械出身,大学毕业了都不知道什么叫代码,在58干了一段销售.实在是干不下去了,不干了就得在网上找工作,就喜欢干电脑相关的工作,有一天,一家培训机构给我打来电话让我去面试,我以为让我去工作那,谁知道是忽悠我去培训,我当然不可能花钱了,就直接回去了,想了几天,除了销售我真不知道干啥,咬咬牙,就花钱培训了.我擦,咋说这到了,不跟你们说了,还是写下面的这个功能吧,虽然很简单,但

修改flex4 List的多选模式

设置list的allowMultipleSelection为true就可以启用多选,支持"ctrl"多选和"shif"连选.操作方式和我们平时多选文件的方法基本一致.但这种方式在"进行切换时,会清除当前选择.",也就是说只有按住"ctrl"的时候才能进行多选. 而我希望用户只需要点击就可以进行多选,那么要怎么做呢,看了一下api,貌似没有默认的属性可以设置.于是我决定重写list,结果发现出乎意料的简单,只需重写一个方法即可

表格控件的多选模式总结

公司在推开发平台,使用flex做的,我的一个项目也在用.综合来讲,这个平台不是很好用,一帮子小孩做的系统,缺乏项目经验,也缺乏基本的UI基础,但是公司要推,没办法.今天测试系统,发现了一个表格选中模式的问题(表格的选中模式已经是在平常不过的ui操作模式了,随便一个商品化的表格控件都支持).一方面平台没有做出很好的支持,再有开发人员也缺乏这方面扥经验(还都是具有好几年工作经验的开发人员),做出来的程序是在难用.忍不住就写了这篇文章. ================================

WPF DataGrid ListView 等等 改变 选中行 颜色;以及 不变的原因

WPF中改变选中行的颜色是很简单的,就是用触发器:比如:以DataGrid为例: DataGrid.RowStyle Style TargetType= DataGridRow SetterProperty= Background Value= White / Style .Triggers TriggerProperty= IsMouseOver Value= True SetterProperty= Background Value= LightGray / /Trigger Trigger

【转】Android 带checkbox的listView 实现多选,全选,反选 -- 不错

原文网址:http://blog.csdn.net/onlyonecoder/article/details/8687811 Demo地址(0分资源):http://download.csdn.net/detail/onlyonecoder/5154352 由于listview的一些特性,刚开始写这种需求的功能的时候都会碰到一些问题,重点就是存储每个checkbox的状态值,在这里分享出了完美解决方法: 布局文件: [html] view plaincopy <?xml version="

Android 带checkbox的listView 实现多选,全选,反选 简易版

activity_main.xml <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" a

Android 带checkbox的listView 实现多选,全选,反选,删除

activity_main.xml <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" a

【转】Android 带checkbox的listView 实现多选,全选,反选----解决checkbox错位问题

原文网址:http://blog.csdn.net/onlyonecoder/article/details/8687811 Demo地址(0分资源):http://download.csdn.net/detail/onlyonecoder/5154352 由于listview的一些特性,刚开始写这种需求的功能的时候都会碰到一些问题,重点就是存储每个checkbox的状态值,在这里分享出了完美解决方法: 布局文件: [html] view plaincopy <?xml version="