ListView的多选模式

ListView的多选需求

需求驱动技术,最近在项目中又遇到这样一个需求,简单而言就是:遍历某个文件夹下的所有log文件,然后将它们通过微信发送给别人。
这个功能很容易实现,但是在实现过程中,我希望自己的产品使用起来更加的人性化,所有我添加了多文件压缩打包功能,多选,反选,全选等功能,这样使用者就可以更加合理选择自己需要的log了。
那么问题来了,ListView应该怎么实现多选功能呢?


上下文操作模式

有很多朋友也许不了解上下文操作模式是什么,我们来看Google是怎么说的:

上下文操作模式是 ActionMode 的一种系统实现,它将用户交互的重点转到执行上下文操作上。用户通过选择项目启用此模式时,屏幕顶部将出现一个“上下文操作栏”,显示用户可对当前所选项执行的操作。 启用此模式后,用户可以选择多个项目(若您允许)、取消选择项目以及继续在 Activity 内导航(在您允许的最大范围内)。 当用户取消选择所有项目、按“返回”按钮或选择操作栏左侧的“完成”操作时,该操作模式将会禁用,且上下文操作栏将会消失。

该模式还提供了一些方法

//判断一个item是否被选中
public boolean isItemChecked(int position);
//获得被选中item的总数
public int getCheckedItemCount();
//选中一个item
public void setItemChecked(int position, boolean value);
//清除选中的item
public void clearChoices();

希望了解得更加详细,可以参考菜单

从上面我们知道了这些信息:

1、进入上下文模式,就可以进行多选操作

2、当用户取消所有选项的时候,该操作模式被禁用

3、按下返回键和“完成”按钮,该操作模式被禁用

值得一提的是,第2项有一些坑,在下面我会提到。
第3项,造成了一个我还没有解决的bug,我期待按下”完成”按钮以后,将压缩所选log文件,而按下“返回”键,不做任何操作,退出上下文模式。然而我没有找到区分这两个键的办法
我尝试在上下文模式中,监听”back”键,然而back键的响应,居然在上下文模式消失以后,所以还是没有办法区分。
好了,我自己的迷惑就说到这里,下面我们来看listView怎么进入上下文模式。


ListView的上下文模式,CHOICE_MODE_MULTIPLE_MODAL

ListView有四种模式:

/**  
 * Normal list that does not indicate choices  
 * 普通模式
 */  
public static final int CHOICE_MODE_NONE = 0;  
/**  
 * The list allows up to one choice  
 * 单选模式,不常用
 */  
public static final int CHOICE_MODE_SINGLE = 1;  
/**  
 * The list allows multiple choices  
 * 多选模式,无排除性
 */  
public static final int CHOICE_MODE_MULTIPLE = 2;  
/**  
 * The list allows multiple choices in a modal selection mode  
 * 多选模式,有排除性
 */  
public static final int CHOICE_MODE_MULTIPLE_MODAL = 3;

说说CHOICE_MODE_MULTIPLECHOICE_MODE_MULTIPLE_MODAL的区别
CHOICE_MODE_MULTIPLE:进入该多选模式以后,点击item,item除了被选中,还会响应它的onClick()事件
CHOICE_MODE_MULTIPLE_MODAL:进入该多选模式以后,点击item,item被选中,不响应点击事件

于是根据我自己的需求,我选中了CHOICE_MODE_MULTIPLE_MODAL模式,因为我需要在不进入多选模式前,点击item,就看发送单个log。

在代码中,进入多选模式:

private void initActionMutiply(){
        //多选模式
        loglist.setChoiceMode(ListView.CHOICE_MODE_MULTIPLE_MODAL);
        //监听模式状态
        if(mCallback==null) mCallback = new ModeCallback();
        loglist.setMultiChoiceModeListener(mCallback);  
        //清除所有选中
        loglist.clearChoices();
    }

在调用上面的代码以后,我们只要长按item,就会自动进入多选模式。


监听多选模式,修改ActionBar样式

先来看看我的效果图,避免暴露隐私,我打了码。。

ListView的多选模式_第1张图片ListView的多选模式_第2张图片

进入多选模式以后,我希望可以显示当前的选中项,但是由于在这个模式中,item被选中以后,是不会产生样式变化的,所以在adapter里面,我们要检查item的状态,主动修改其样式,例如这里我就讲背景颜色变成蓝色

@Override
    public void convert(ViewHolder holder, LogInfo t) {
        holder.setText(R.id.name, "log名称:"+t.logName);
        holder.setText(R.id.createTime, "创建时间:"+t.getFormatTime());
        holder.setText(R.id.size, "文件大小:"+t.getSize());

        if (mListView.isItemChecked(holder.getmPosition())) {//判断是否被选中
            holder.setBackgroundColor(R.id.log_list_item_layout, Color.BLUE);
        } else {
            holder.setBackgroundColor(R.id.log_list_item_layout, Color.WHITE);
        }       
    }   

另外我希望看到我选中了多少项,所以有必要自定义一下ActionBar的布局,当然我们还希望退出多选模式以后,回复原来的ActionBar,所以这里我们有一个监听器

private class ModeCallback implements ListView.MultiChoiceModeListener {
        private View mMultiSelectActionBarView;
        private TextView mSelectedCount;
        private ArrayList arr = new ArrayList();

        @Override
        /**
        * 进入多选模式,这个方法首先被调用
        * 在这里我们新建菜单(多选,反选等)
        * 修改ActionBar样式,使其可以显示当前选中的item数目
        */
        public boolean onCreateActionMode(ActionMode mode, Menu menu) {  
            // actionmode的菜单处理
            MenuInflater inflater = getMenuInflater();
            inflater.inflate(R.menu.multi_select_menu, menu);//新建菜单
            if (mMultiSelectActionBarView == null) {
                mMultiSelectActionBarView = LayoutInflater.from(LogListActivity.this)
                    .inflate(R.layout.list_multi_select_actionbar, null);
                mSelectedCount =
                    (TextView)mMultiSelectActionBarView.findViewById(R.id.selected_conv_count);
                ((TextView)mMultiSelectActionBarView.findViewById(R.id.title)).setText("已选择");                
            }
            mode.setCustomView(mMultiSelectActionBarView);//设置新的ActionBar样式
            return true;
        }
        @Override
        public boolean onPrepareActionMode(ActionMode mode, Menu menu) {                        
            return false;
        }
        @Override
        public boolean onActionItemClicked(ActionMode mode, MenuItem item) {         
            switch (item.getItemId()) {                         
            case R.id.action_slelect_all:  
                selectAll();
                return true;  
            case R.id.action_slelect_opposite:  
                selectOpposite();
                return true;  
            case R.id.action_slelect_cancel:
                loglist.clearChoices();
                mode.finish();      
                return true;  
            case R.id.action_slelect_share:     
                shareChoices();
                return true;   
            case R.id.action_slelect_delete:         
                selectDelete();
                return true;  
            default:  
                return true;  
            }
        }
        //分享        
        private synchronized void shareChoices(){
            arr.clear();
            for(int i= 0; i< mLogListAdapter.getCount(); i++){          
                if(loglist.isItemChecked(i)){
                    arr.add(LogShareHelper.logArray.get(i));
                }
            }   
            if(arr.size()!=0) shareAllLogs(arr);
        }

        /**
        * 退出多选模式的时候,这个方法会被调用
        */
        @Override
        public void onDestroyActionMode(ActionMode mode) {        
            isFirst = false;
            loglist.clearChoices();
        }
        /**
        * 当item选中状态改变时调用
        * 在这里修改通知adapter修改item背景色,更新选中数目
        */
        @Override
        public void onItemCheckedStateChanged(ActionMode mode,
                int position, long id, boolean checked) {       
            if(isFirst){
                mSelectedCount.setText("0");
                isFirst = false;
            }else{
                mSelectedCount.setText(loglist.getCheckedItemCount()+"");
            }
            mLogListAdapter.notifyDataSetChanged();
        }
        //全选功能
        private void selectAll(){
            for(int i= 0; i< mLogListAdapter.getCount(); i++){
                loglist.setItemChecked(i, true);
            }
        }
        //反选
        private void selectOpposite(){
            ...
        }
        //删除选中
        private void selectDelete(){
            ...
            }
        }
   }

在上面的代码中看到,ListView.MultiChoiceModeListener可以监听多选模式的状态,所以我根据这些状态,做相应的操作。


写在最后

在前文中我们注意到,进入多选模式,可以长按item
但是如果我们希望通过点击一个按钮,进入多选模式怎么办呢?
要进入多选模式,就必须有一个item被选中,但是如果我们调用

mListView.setItemChecked(0,true);  

来使第一项被选中,从而进入多选模式。这样会造成用户体验问题,也许用户并不想选中第一项。
那么,我之后调用

mListView.setItemChecked(0,false);  

来清除第一项的选中,是不是就可以了呢?
现在请参考上面提到的上下文菜单第2项特性,一旦没有选中项,就会退出多选模式
所以解决办法是,接下来再调用一句

mListView.clearChoices();  

就可以清除选中状态了,而且不会退出多选模式(并且不会调用监听器的onItemCheckedStateChanged(ActionMode mode,int position, long id, boolean checked)方法)。

参考源码下载

你可能感兴趣的:(android开发,listview,Android,多选)