ListView的两种多选模式

本文探讨了ListView的两种多选模式:一种是通过 SparseBooleanArray 实现的单击选择,另一种是 CHOICE_MODE_MULTIPLE_MODAL 模式,需要设置 MultiChoiceModeListener 并处理 ActionBar 的布局和菜单项。在该模式下,长按或点击菜单项进入多选,提供全选和取消全选功能。代码示例展示了如何实现这些功能。

在上篇博客中,详细记录了ListView的各种用法,本篇博客主要用于探究ListView的两种多选模式。

在上篇博客的结尾,说到在多选模式中,ListView选中的列表项都保存在一个SparseBooleanArray中,利用ListView的列表是否被选中的状态,可以突出显示被选中的项,比如给选中的列表项设置背景色

布局文件依然是只有一个ListView

<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="match_parent" >

    <ListView 
        android:id="@+id/list"
        android:layout_width="match_parent"
        android:layout_height="match_parent"/>

</RelativeLayout>

MainActivity.java

public class MainActivity extends ActionBarActivity {

    ListView listView;
    List<String> datas = new ArrayList<String>();
    ListViewAdapter adapter;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        listView = (ListView)findViewById(R.id.list);
        initData();
//        adapter = new ArrayAdapter<String>(this, R.layout.item, datas);
        adapter = new ListViewAdapter();
        listView.setAdapter(adapter);
        listView.setChoiceMode(ListView.CHOICE_MODE_MULTIPLE);
        listView.setOnItemClickListener(new OnItemClickListener() {
            @Override
            public void onItemClick(AdapterView<?> parent, View view,
                    int position, long id) {
                // TODO Auto-generated method stub
                //点击item后,通知adapter重新加载view
                adapter.notifyDataSetChanged();
            }
        });
    }

    class ListViewAdapter extends BaseAdapter {
        @Override
        public int getCount() {
            // TODO Auto-generated method stub
            return datas.size();
        }

        @Override
        public Object getItem(int position) {
            // TODO Auto-generated method stub
            return datas.get(position);
        }

        @Override
        public long getItemId(int position) {
            // TODO Auto-generated method stub
            return position;
        }

        @Override
        public View getView(int position, View convertView, ViewGroup parent) {
            // TODO Auto-generated method stub
            ViewHolder viewHolder;
            if(convertView == null) {
                viewHolder = new ViewHolder();
                convertView = View.inflate(MainActivity.this, R.layout.item, null);
                viewHolder.text = (TextView)convertView.findViewById(R.id.text);
                convertView.setTag(viewHolder);
            } else {
                viewHolder = (ViewHolder)convertView.getTag();
            }
            viewHolder.text.setText(datas.get(position));
            //判断position位置是否被选中,改变颜色
            if(listView.isItemChecked(position)) {
                convertView.setBackgroundColor(Color.RED);
            } else {
                convertView.setBackgroundColor(Color.TRANSPARENT);
            }
            return convertView;
        }

        class ViewHolder {
            TextView text;
        }
    }

    public void initData() {
        for(int i = 1; i <= 15; i++) {
            datas.add(i + "");
        }
    }
}

主要代码一共没有几行,点击列表项后,在onItemClick中通知列表刷新数据,在getView时,判断点击的列表项被选中的状态,如果被选中,设置背景色为红色,否则设置为透明色

运行程序,点击列表项

这里写图片描述

点击某个列表后,背景色变为红色,代表被选中,再次点击同一个列表项后,背景色又变为透明色,代表取消选中

ListView的CHOICE_MODE_MULTIPLE_MODAL模式

这是ListView的另一个多选模式,通过长按ListView的列表项或主动调用setItemChecked方法选中某个列表项进入该模式,这种模式下不会触发ListView的onClick事件。设置为这种模式后,需要给ListView添加MultiChoiceModeListener事件监听,实现其中的几个方法。

可以通过点击菜单项和长按列表项进入多选模式,进入多选模式后,ActionBar的布局和菜单项会发生改变,可以在ActionBar上显示选中的条目数,所以首先要定义ActionBar的布局文件和菜单文件

布局文件actionbar_view.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"
    android:orientation="horizontal" >

    <TextView 
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="已选中条目数:"/>

    <TextView 
        android:id="@+id/selected_num"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"/>

</LinearLayout>

菜单文件

进入多选模式前的菜单main.xml

<menu xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    tools:context="com.example.testlistview.MainActivity" >

    <item
        android:id="@+id/enter_mode"
        android:orderInCategory="100"
        android:title="进入多选模式"
        app:showAsAction="never"/>

</menu>

进入多选模式后的菜单multiple_mode_menu.xml

<menu xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    tools:context="com.example.testlistview.MainActivity" >

    <item
        android:id="@+id/select_all"
        android:orderInCategory="100"
        android:title="全选"
        android:showAsAction="never"/>

    <item
        android:id="@+id/unselect_all"
        android:orderInCategory="200"
        android:title="取消全选"
        android:showAsAction="never"/>

</menu>

在正常模式下,有一个菜单项,点击菜单项或者长按列表项进入多选模式,进入多选模式后,ActionBar上的View布局显示选中条目的数目,有2个菜单项:全选、取消全选

下面是MainActivity的代码

public class MainActivity extends ActionBarActivity {

    ListView listView;
    List<String> datas = new ArrayList<String>();
    ListViewAdapter adapter;
    ModeCallback callback;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        listView = (ListView)findViewById(R.id.list);
        initData();
        adapter = new ListViewAdapter();
        listView.setAdapter(adapter);
        listView.setChoiceMode(ListView.CHOICE_MODE_MULTIPLE_MODAL);
        callback = new ModeCallback();
        listView.setMultiChoiceModeListener(callback);
    }

    class ModeCallback implements MultiChoiceModeListener {

        View actionBarView;
        TextView selectedNum;

        @Override
        public boolean onPrepareActionMode(ActionMode mode, Menu menu) {
            // TODO Auto-generated method stub
            return true;
        }

        //退出多选模式时调用
        @Override
        public void onDestroyActionMode(ActionMode mode) {
            // TODO Auto-generated method stub
            listView.clearChoices();
        }

        //进入多选模式调用,初始化ActionBar的菜单和布局
        @Override
        public boolean onCreateActionMode(ActionMode mode, Menu menu) {
            // TODO Auto-generated method stub
            getMenuInflater().inflate(R.menu.multiple_mode_menu, menu);
            if(actionBarView == null) {
                actionBarView = LayoutInflater.from(MainActivity.this).inflate(R.layout.actionbar_view, null);
                selectedNum = (TextView)actionBarView.findViewById(R.id.selected_num);
            }
            mode.setCustomView(actionBarView);
            return true;
        }

        //ActionBar上的菜单项被点击时调用
        @Override
        public boolean onActionItemClicked(ActionMode mode, MenuItem item) {
            // TODO Auto-generated method stub
            switch(item.getItemId()) {
                case R.id.select_all:
                    for(int i = 0; i < adapter.getCount(); i++) {
                        listView.setItemChecked(i, true);
                    }
                    updateSelectedCount();
                    adapter.notifyDataSetChanged();
                    break;
                case R.id.unselect_all:
                    listView.clearChoices();
                    updateSelectedCount();
                    adapter.notifyDataSetChanged();
                    break;
            }
            return true;
        }

        //列表项的选中状态被改变时调用
        @Override
        public void onItemCheckedStateChanged(ActionMode mode, int position,
                long id, boolean checked) {
            // TODO Auto-generated method stub
            updateSelectedCount();
            mode.invalidate();
            adapter.notifyDataSetChanged();
        }

        public void updateSelectedCount() {
            int selectedCount = listView.getCheckedItemCount();
            selectedNum.setText(selectedCount + "");
        }
    }

    class ListViewAdapter extends BaseAdapter {
        @Override
        public int getCount() {
            // TODO Auto-generated method stub
            return datas.size();
        }

        @Override
        public Object getItem(int position) {
            // TODO Auto-generated method stub
            return datas.get(position);
        }

        @Override
        public long getItemId(int position) {
            // TODO Auto-generated method stub
            return position;
        }

        @Override
        public View getView(int position, View convertView, ViewGroup parent) {
            // TODO Auto-generated method stub
            ViewHolder viewHolder;
            if(convertView == null) {
                viewHolder = new ViewHolder();
                convertView = View.inflate(MainActivity.this, R.layout.item, null);
                viewHolder.text = (TextView)convertView.findViewById(R.id.text);
                convertView.setTag(viewHolder);
            } else {
                viewHolder = (ViewHolder)convertView.getTag();
            }
            viewHolder.text.setText(datas.get(position));
            if(listView.isItemChecked(position)) {
                convertView.setBackgroundColor(Color.RED);
            } else {
                convertView.setBackgroundColor(Color.TRANSPARENT);
            }
            return convertView;
        }

        class ViewHolder {
            TextView text;
        }
    }

    public void initData() {
        for(int i = 1; i <= 15; i++) {
            datas.add(i + "");
        }
    }

    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        // Inflate the menu; this adds items to the action bar if it is present.
        getMenuInflater().inflate(R.menu.main, menu);
        return true;
    }

    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        int id = item.getItemId();
        if (id == R.id.enter_mode) {
            listView.setItemChecked(0, true);
            listView.clearChoices();
            callback.updateSelectedCount();
        }
        return super.onOptionsItemSelected(item);
    }
}

长按某个列表项进入多选模式后,会自动选中长按的item,这个是正常的,但是如果点击菜单项进入多选模式,只能通过调用listView的setItemChecked方法选中一个item进入,但是这种情况在用户看来是不可理解的,所以在onOptionsItemSelected(MenuItem item)中点击菜单进入多选模式后,要调用listView.clearChoices()清空选中的列表项,这样就不会选中任何列表项

接下来定义了一个实现MultiChoiceModeListener接口的类,实现了接口中的几个方法

@Override
        public boolean onCreateActionMode(ActionMode mode, Menu menu) {
            ...
        }

进入多选模式时,会调用这个方法来初始化ActionBar上的布局和菜单项。在方法中,加载了菜单文件和布局文件,并调用mode.setCustomView(actionBarView)设置ActionBar的布局

@Override
        public boolean onActionItemClicked(ActionMode mode, MenuItem item) {
        ...
    }

进入多选模式后,点击ActionBar上的菜单项会触发此方法,多选模式下的菜单有2个:全选和取消全选,点击全选后执行的代码是

    for(int i = 0; i < adapter.getCount(); i++) {
        listView.setItemChecked(i, true);
    }
    updateSelectedCount();
    adapter.notifyDataSetChanged();
    break;

遍历并设置adapter中的所有item项为选中状态,然后更新Action View中的选中数目,最后调用notifyDataSetChanged方法通知ListView刷新列表

点击取消全选执行的代码是

    listView.clearChoices();
    updateSelectedCount();
    adapter.notifyDataSetChanged();
    break;

调用clearChoices()清空LilistView的选中项,并更新View的选中数目,最后同样要通知ListView刷新列表

然后是

@Override
        public void onItemCheckedStateChanged(ActionMode mode, int position, long id, boolean checked) {
            ...
        }

进入多选模式后点击每个列表项都会回调此方法,方法中的逻辑很简单,更新选中数目,并通知ListView刷新列表

最后是onDestroyActionMode(ActionMode mode)方法,方法中只有一行代码,在退出多选模式后清空ListView中的选中项即可

运行效果,正常模式下

这里写图片描述

进入多选模式

这里写图片描述

点击选中列表项

这里写图片描述

全选

这里写图片描述

取消全选

这里写图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值