
正文
ListView多选和单选模式重新整理
提示:扫一扫查出行【扫一扫了解最新限行尾号】
复制提示
-
超简单的单选和多选ListView
在开发过程中,我们经常会使用ListView去呈现列表数据,比如商品列表,通话记录,联系人列表等等,在一些情况下,我们还需要去选择其中的一些列表数据进行编辑。以前,我在项目开发中,都是在自定义的Adapter中去维护一个SparseBooleanArray变量来保存当前ListView中已经被选中的项,然后在自定义Adapter的getView()和ListView的setOnItemClickListener()方法中去实时更新SparseBooleanArray变量,从而当用户选择提交数据的时候,直接遍历SparseBooleanArray中的值就可以了,这种做法,虽然也能实现功能,但是无疑增加了代码开销。
今天,在看文档的时候,发现了一个更好的解决方案(很多人已经用过了吧):使用ListView的choiceMode,官方文档见如下:
根据上面的文档说明,可以知道,android:choiceMode有以下几个值:默认(不设置android:choiceMode属性,即不支持单选或多选),singleChoice(单选),multipleChoice(多选),mutipleChoiceModal(特殊多选模式,可以通过设置MultiChoiceModeListener进行监听选择模式,类似ActionBar的ActionMode)。在XML中给ListView设置了android:choiceMode属性一个值后,我们还需要给ListView一个适配器,这里我们使用默认的ArrayAdapter:
//如果是单选模式,则可以使用 android.R.layout.simple_list_item_single_choice
ArrayAdapter<String> myAdapter = new ArrayAdapter<String>(this, android.R.layout.simple_list_item_multiple_choice, mDatas);
mListView.setAdapter(myAdapter);
这样我们就已经完成了ListView的多选或单选实现。
另外一个问题是:我们怎样才能获取当前ListView中被选中的那些项呢?
我们其实可以通过ListView的isItemChecked(int position)方法判断一项是否被选中,或直接使用ListView.getCheckedItemPositions()来获取所有选中的项,这个方法返回一个SparseBooleanArray对象,遍历它就可以获取所有选中的项。
-
自定义ListView的多选和单选项布局
对于一些简单的列表,上面的方法可能已经能够满足需求。其实,上面的列表项只显示了一个标题和一个复选框,但在实际开发中,UE或产品经理可能要求我们去实现的列表远比上面的列表复杂得多,所以往往就需要使用自定义的Adapter来填充ListView。
但是,如果我们使用自定义的Adapter来填充ListView,那怎么让我们自定义的Checkbox能够无缝衔接ListView的选择状态呢?
一种普遍的做法是在重写自定义Adapter的getView()时,先通过convertView.findViewById()获取到Checkbox后,通过mList.isItemChecked(int position)判断当前position的状态后,再去更新Checkbox的选择状态。
这里,我介绍的是另外一种方法。
首先,我们先来看下为什么我们使用android.R.layout.simple_list_item_multiple_choice布局来填充ArrayAdapter时,不需要我们自己去维护CheckBox的选择状态?
查看ListView的源码,在ListView的setupChild方法中,有下面的一段代码:
if (mChoiceMode != CHOICE_MODE_NONE && mCheckStates != null) {
if (child instanceof Checkable) {
((Checkable) child).setChecked(mCheckStates.get(position));
} else if (getContext().getApplicationInfo().targetSdkVersion >= android.os.Build.VERSION_CODES.HONEYCOMB)
{
child.setActivated(mCheckStates.get(position));
}
}
即如果ListView的child(从自定义的Adapter的getView()方法中返回的View)实现了Checkable接口,那么当listView的项选择状态改变时,listView也会去同步更新这个child的状态(android 3.1或3.1以上平台,会触发setActivated方法),其实simple_list_item_multiple_choice.xml中只有一个CheckedTextView
<CheckedTextView
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@android:id/text1"
android:layout_width="match_parent"
android:layout_height="?android:attr/listPreferredItemHeightSmall"
android:textAppearance="?android:attr/textAppearanceListItemSmall"
android:gravity="center_vertical"
android:checkMark="?android:attr/listChoiceIndicatorMultiple"
android:paddingStart="?android:attr/listPreferredItemPaddingStart"
android:paddingEnd="?android:attr/listPreferredItemPaddingEnd" />
而CheckedTextView 是实现了Checkable接口的,所以当我们使用simple_list_item_multiple_choic.xml布局作为Adapter的getView()的返回值时,是不需要我们额外去关心Checkbox的状态问题。
通过上面的分析,我们自定义一个View时,只需要实现了Checkable接口,那么就不用我们在getView中去额外维护选中状态了。如果android3.1或android3.1以上的平台,我们还可以重写setActivated方法来更新我们的选中状态。相关示例代码如下:
package com.shaoxiong.li.marvel.myapplication; 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; /**
* Created by lishaoxiong on 16-2-22.
*/
public class CustomCheckTextView extends LinearLayout implements Checkable { private TextView titleView;
private CheckBox mCheckBox; public CustomCheckTextView(Context context) {
this(context, null);
} public CustomCheckTextView(Context context, AttributeSet attrs) {
this(context, attrs, 0);
} public CustomCheckTextView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr); LayoutInflater mLayoutInflater = LayoutInflater.from(context);
//将加载出来的View添加到当前View层级中去。
//有两种方案,一种是加载布局时将rootView传进去,或直接使用addView添加进去
//View v = mLayoutInflater.inflate(R.layout.layout_custom_ctv, null);
View v = mLayoutInflater.inflate(R.layout.layout_custom_ctv, this, true);
titleView = (TextView)v.findViewById(R.id.headListView_item_text);
mCheckBox = (CheckBox)v.findViewById(R.id.headListView_item_cb);
//this.addView(v);
} @Override
public void setChecked(boolean checked) {
mCheckBox.setChecked(checked);
} @Override
public boolean isChecked() {
return mCheckBox.isChecked();
} @Override
public void toggle() {
mCheckBox.toggle();
} public void setTitle(String title) {
titleView.setText(title);
} @Override
public void setActivated(boolean activated) {
super.setActivated(activated);
}
}
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="horizontal"
android:padding="20dp">
<CheckBox
android:id="@+id/headListView_item_cb"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:focusable="false"
android:clickable="false"
android:focusableInTouchMode="false"/> <TextView
android:id="@+id/headListView_item_text"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginRight="10dp"/> </LinearLayout>
public class MyAdapter extends BaseAdapter {
private Context mContext;
private ArrayList<String> dataList;
public MyAdapter(Context context, ArrayList<String> dataList) {
this.mContext = context;
this.dataList= dataList;
}
@Override
public int getCount() {
return dataList.size();
}
@Override
public Object getItem(int position) {
return dataList.get(position);
}
@Override
public long getItemId(int position) {
return position;
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
ViewHolder viewHolder = null;
if(convertView == null) {
convertView = LayoutInflater.from(mContext).inflate(R.layout.item_headlistview, null);
viewHolder = new ViewHolder();
//viewHolder.mTextView = (TextView)convertView.findViewById(R.id.headListView_item_text);
// viewHolder.checkedTv = (CheckedTextView)convertView.findViewById(R.id.item_checked_tv);
viewHolder.customCheckTextView = (CustomCheckTextView)convertView;
convertView.setTag(viewHolder);
}else {
viewHolder = (ViewHolder)convertView.getTag();
}
// viewHolder.checkedTv.setText(dataList.get(position));
// viewHolder.mTextView.setText(dataList.get(position));
viewHolder.customCheckTextView.setTitle(dataList.get(position));
return convertView;
}
}
static class ViewHolder {
// TextView mTextView;
// CheckedTextView checkedTv;
CustomCheckTextView customCheckTextView;
}
这样,通过上面的方法,你就可以去实现各种自己自定义好布局的多选或单选列表了。







