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

这样,通过上面的方法,你就可以去实现各种自己自定义好布局的多选或单选列表了。

时间: 2024-10-13 00:01:12

ListView多选和单选模式重新整理的相关文章

ListView 的单选模式

<RadioButton与ListView的混合使用>一文中,我在适配器中用标记的方法实现了用户选择的操作,这次用ListView的单选模式来实现一下.ListView的默认状态下是没有选择行为的,把ListView的choiceMode设置为singleChoice,列表就可以实现单选(当然它也有多选模式,这个后面再研究). Activity的布局文件如下,ListView选择了单选模式,这次我把ListView上方的TextView换成了Button: <LinearLayout x

ListView多选操作模式详解CHOICE_MODE_MULTIPLE与CHOICE_MODE_MULTIPLE_MODAL

这篇文章我们将详细的介绍如何实现ListView的多选操作,文中将会纠正在使用ListViewCHOICE_MODE_MULTIPLE或者CHOICE_MODE_MULTIPLE_MODAL时容易犯的错误,以及 CHOICE_MODE_MULTIPLE与CHOICE_MODE_MULTIPLE_MODAL的区别.最后我们将给出一个demo来演示两种多选操作的实现. 一.在不使用ListView多选模式的情况下 注:我认为这一节可以不看,因为我觉得不使用ListView的多选模式有点愚蠢. 如果我

iOS——UITableView单选模式,多选模式,单选多选混合模式

70行代码量的UITableViewCell实现单选,多选,单选多选混合选择. SingleVC——50行 MultipleVC——55行 ChaosVC——80行 cell为Xib拓展性较好,可拿去直接使用. 代码量不是越少越好,还要容易阅读,这里突出代码量的意思仅仅是建立在简单易用的原则上,有不明白可以跟帖,有大神优化的话跪求恩赐. github: https://github.com/ZyZwei/iOS_SelectStyle.git coding: https://git.coding

Android实战之ListView复选框

项目中有用到复选框的例子,啊啊......在网上查找有关资料,大多都是过于繁琐,所以自己决定写个这个方面的demo... 先给个效果图: 在ListView中添加复选框主要注意以下几个问题: 1.ListView item与item中的控件抢焦点的问题(必须设置CheckBox不可点击和不能获取焦点,让ListView得到焦点) 2.ListView的setChoicMode(int choiceMode)选择模式():有choiceMode : CHOICE_MODE_NONE, CHOICE

[CSS]复选框单选框与文字对齐问题的研究与解决.

前言:今天碰到的这个问题, 恰好找到一个很好的博文, 在这里转载过来 学习下. 原文地址:复选框单选框与文字对齐问题的研究与解决. 目前中文网站上面的文字,就我的个人感觉而言,绝大多数网站的主流文字大小为12px,因为在目前高分辨率显示器屏幕下,11px的汉字,其像素点开始不规整,文字不如12px来的显示良好.12px大小的文字就是主流也是底线.然而12px的文字与单选框和复选框是不对齐的.例如下面这张雅虎中国首页在火狐浏览器下的截图: 雅虎中国首页单选框复选框与文字不对齐 这里,不是说,雅虎中

安装完linux&#39;后忘记选组件或者模式

查看模式包 yum grouplist  或   yum grouplist yum groupinstall "compatibility libraries" "base" "development tools" yum groupinstall "performance tools" "debugging tools" "dial-up networking support" yu

php 查找数组中是否存在某项,并返回指定的字符串,可用于检查复选,单选等

/** * 查找数组中是否存在某项,并返回指定的字符串,可用于检查复选,单选等 * @param $id * @param $ids * @param string $returnstr * @return string */ function check_in($id,$ids,$returnstr = 'checked') { if(in_array($id,$ids)) return $returnstr; }

复选框单选框与文字对齐问题解决

css代码如下: vertical-align:middle; margin-top:-2px; margin-bottom:1px; 原文链接:复选框单选框与文字对齐问题的研究与解决

ListView 自定义BaseAdapter实现单选打勾(无漏洞)

最近因为一个项目的原因需要自定义一个BaseAdapter实现ListVIew单选打勾的功能,虽然听起来很简单,我在网上也 看过一些例子,似乎是实现了,但往往存在一些漏洞.往往漏洞如下 1.网上例子item较少,item增多时漏洞出现,忽略了BaseAdapter中getView()方法中convertView重用的问题 2.忽略了BaseAdapter中getView()方法并不是一下子加载完所有item,上下拖动listview时item会重新加载,getview会重新被调用,所以上下拖动的