zoukankan      html  css  js  c++  java
  • ListView的操作模式的选择的更详细的解释CHOICE_MODE_MULTIPLE与CHOICE_MODE_MULTIPLE_MODAL

    本文介绍了我们将如何取得具体ListView多选择操作。本文将正确使用ListViewCHOICE_MODE_MULTIPLE要么CHOICE_MODE_MULTIPLE_MODAL时间easy误区。以及

    CHOICE_MODE_MULTIPLE与CHOICE_MODE_MULTIPLE_MODAL的差别。

    最后我们将给出一个demo来演示两种多选操作的实现。


    一、在不使用ListView多选模式的情况下

    注:我觉得这一节能够不看,由于我觉得不使用ListView的多选模式有点愚蠢。

    假设我们不知道ListView自带多选模式,那么我们通常是通过维护一个保存被选择position集合来实现多选的。通常情况下这个集合类型我们选择HashSet。

    实现的大致框架例如以下:

    Adapter中:

    保存被选择的position

    public HashSet<Long> selectedItems = new HashSet<Long>();

    getView中推断当前Position是否在集合中。从而显示不同的外观

        public View getView(int position, View convertView, ViewGroup par) {
    ......
                if(selectedItems.contains((long)position)){
                    holder.cBox.setChecked(true);
                }else{
                    holder.cBox.setChecked(false);
                }
                if(selectedMode==AppContext.MULTI_SELECTED){
                    holder.cBox.setVisibility(View.VISIBLE);
                    holder.check_box_wraper.setVisibility(View.VISIBLE);
                }else{
                    holder.cBox.setVisibility(View.GONE);
                    holder.check_box_wraper.setVisibility(View.GONE);
                }
    .....
    }

    Activity中:

    主要是处理onItemClick事件,在不同模式下。做不同的处理。

    @Override
    public void onItemClick(AdapterView<?

    > a, View v, int position, long id) { //普通模式 :直接打开一个activity if(itemClickActionMode==AppContext.VIEW_NOTE){ Long mId=Long.parseLong(idText.getText().toString()); Uri uri = ContentUris.withAppendedId(getIntent().getData(), mId); startActivity(new Intent(Intent.ACTION_VIEW, uri)); } //多选模式:更新adapter中selectedItems 集合的值,同一时候 让adapter在getView中改变item的外观。 else{ ViewHolder vHollder = (ViewHolder) v.getTag(); if(mAdapter.selectedItems.contains((long)position)){ mAdapter.selectedItems.remove((long)position); }else{ mAdapter.selectedItems.add((long)position); } mAdapter.notifyDataSetChanged(); onItemSelected(getSelectedCount()); } }


    上面的做法其有用的非常普遍。可是我们不提倡。


    二、使用ListViiewCHOICE_MODE_MULTIPLE模式


    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_NONE是普通模式,CHOICE_MODE_SINGLE是单选模式,不经常使用,CHOICE_MODE_MULTIPLECHOICE_MODE_MULTIPLE_MODAL都是多选模式,他们的差别稍后我们会讲到。

    所以ListView在设计的时候事实上是考虑了多选操作的。我们没有必要自己再像第一节描写叙述的那样专门维护一个HashSet来保存被选择的position。

    实现ListView的多选操作的代码在ListView直接父类AbsListView中,AbsListView已经有一个mCheckStates变量来做了保存被选择的position这个事情。

    mCheckStates的定义例如以下:

    1
    SparseBooleanArray mCheckStates;

    AbsListView还定义了例如以下公共方法:

    //推断一个item是否被选中

    1
    public boolean isItemChecked(int position);

    //获得被选中item的总数

    1
    public int getCheckedItemCount();

    //选中一个item

    1
    public void setItemChecked(int position, boolean value);

    //清除选中的item

    1
    public void clearChoices();

    当点击一个item的时候absListView中会调用performItemClick,假设是CHOICE_MODE_MULTIPLE。则该item点击一次。mCheckStates中对应位置的状态变更一次。然后我们就能够通过listView的getCheckedItemCount()方法获取选择了多少个;isItemChecked(int position)方法推断一个item是不是被选中。

    有了这些原生sdk的支持,难道还有什么多选操作是不能实现的吗?所以是不是应该考虑放弃第一节中描写叙述的那种方法了呢?遗憾的是非常多android开发人员即使是用了CHOICE_MODE_MULTIPLE,仍然没有去利用这些ListView自带的功能。预计是根本不知道该CHOICE_MODE_MULTIPLE的 特性吧,这事实上也是android程序猿与ios程序猿真正存在差距的地方。


    CHOICE_MODE_MULTIPLE实战


    先看看效果图

    package com.example.listmultichoise;
    import android.os.Bundle;
    import android.app.ActionBar;
    import android.app.Activity;
    import android.util.Log;
    import android.view.ActionMode;
    import android.view.LayoutInflater;
    import android.view.Menu;
    import android.view.MenuInflater;
    import android.view.MenuItem;
    import android.view.View;
    import android.view.ViewGroup;
    import android.view.Window;
    import android.widget.AdapterView;
    import android.widget.AdapterView.OnItemClickListener;
    import android.widget.ListView;
    import android.widget.TextView;
    import android.widget.Toast;
    public class ChoiceModeMultipleActivity extends Activity {
        ListView mListView = null;
        MyListAdapter mAdapter;
        private View mMultiSelectActionBarView;
        private TextView mSelectedCount;
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                           
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            getWindow().requestFeature(Window.FEATURE_ACTION_BAR);
            setContentView(R.layout.activity_list);
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                               
            mListView = (ListView)findViewById(R.id.list);
            mAdapter = new MyListAdapter(this,mListView);
            mListView.setAdapter(mAdapter);
            mListView.setChoiceMode(ListView.CHOICE_MODE_MULTIPLE);
            mListView.setOnItemClickListener(new OnItemClickListener() {
                public void onItemClick(AdapterView<?> parent, View view, int position,
                        long id) {
                    mAdapter.notifyDataSetChanged();
                    updateSeletedCount();
                }
            });
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                               
            if (mMultiSelectActionBarView == null) {
                mMultiSelectActionBarView = LayoutInflater.from(ChoiceModeMultipleActivity.this)
                    .inflate(R.layout.list_multi_select_actionbar, null);
                mSelectedCount =
                    (TextView)mMultiSelectActionBarView.findViewById(R.id.selected_conv_count);
            }
            getActionBar().setDisplayOptions(ActionBar.DISPLAY_SHOW_CUSTOM,
                    ActionBar.DISPLAY_SHOW_CUSTOM | ActionBar.DISPLAY_SHOW_HOME |
                    ActionBar.DISPLAY_SHOW_TITLE);
            getActionBar().setCustomView(mMultiSelectActionBarView);
            ((TextView)mMultiSelectActionBarView.findViewById(R.id.title)).setText(R.string.select_item);
        }
        @Override
        public boolean onCreateOptionsMenu(Menu menu) {
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                        
            getMenuInflater().inflate(R.menu.multi_select_menu, menu);
            return true;
        }
        @Override
        public boolean onPrepareOptionsMenu(Menu menu) {
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                        
            MenuItem mItem = menu.findItem(R.id.action_slelect);
            if(mListView.getCheckedItemCount() == mAdapter.getCount()){
                mItem.setTitle(R.string.action_deselect_all);
            }else{
                mItem.setTitle(R.string.action_select_all);
            } 
            return super.onPrepareOptionsMenu(menu);
        }
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                           
        @Override
        public boolean onOptionsItemSelected(MenuItem item) {
            switch (item.getItemId()) {
            case R.id.action_slelect:
                if(mListView.getCheckedItemCount() == mAdapter.getCount()){
                    unSelectedAll();
                }else{
                    selectedAll();
                }
                mAdapter.notifyDataSetChanged();
                break;
            default:
                break;
            }
            return super.onOptionsItemSelected(item);
        }
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                           
        public void selectedAll(){
            for(int i= 0; i< mAdapter.getCount(); i++){
                mListView.setItemChecked(i, true);
            }
            updateSeletedCount();
        }
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                           
        public void unSelectedAll(){
            mListView.clearChoices();
            updateSeletedCount();
        }
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                        
        public void updateSeletedCount(){
            mSelectedCount.setText(Integer.toString(mListView.getCheckedItemCount()));
        }
    }

    代码解释:


    首先设置ListView模式:

    mListView.setChoiceMode(ListView.CHOICE_MODE_MULTIPLE);

    定义一个adapter,当ListView的某个item被选中之后,将该Item的背景设置为蓝色,以标记为选中。不然尽管ListView知道该item被选中,可是界面上没表现出来。
    ......
        public View getView(int position, View convertView, ViewGroup parent) {
            TextView tv;
            if (convertView == null) {
                tv = (TextView) LayoutInflater.from(mContext).inflate(
                        android.R.layout.simple_expandable_list_item_1, parent,
                        false);
            } else {
                tv = (TextView) convertView;
            }
            tv.setText(mStrings[position]);
            updateBackground(position , tv);
            return tv;
        }
        @SuppressLint("NewApi")
        public void updateBackground(int position, View view) {
            int backgroundId;
            if (mListView.isItemChecked(position)) {
                backgroundId = R.drawable.list_selected_holo_light;
            } else {
                backgroundId = R.drawable.conversation_item_background_read;
            }
            Drawable background = mContext.getResources().getDrawable(backgroundId);
            view.setBackground(background);
        }
    ......

    在item每被点击一次中通知adapter,这样做的目的是为了让更新Ui以显示最新的选中状态。
    mListView.setOnItemClickListener(new OnItemClickListener() {
        public void onItemClick(AdapterView<?

    > parent, View view, int position, long id) { mAdapter.notifyDataSetChanged(); updateSeletedCount(); } });

    当中mSelectedCount()作用是在actionbar中更新选中的数目。
    public void updateSeletedCount(){
           mSelectedCount.setText(Integer.toString(mListView.getCheckedItemCount()));
       }

    上面的代码实现了多选操作,可是在我选中一个item的时候,listView的onItemClick也同一时候触发。而一个ListView点击item的兴许操作通常是切换到另外一个界面,所以实际应用中,我们还须要设置一个标志位,用来差别当前是多选状态还是普通状态 。假设是多选状态,调用ListView.setChoiceMode(ListView.CHOICE_MODE_MULTIPLE);  假设是普通状态调用mListView.setChoiceMode(ListView.CHOICE_MODE_NONE);CHOICE_MODE_MULTIPLE模式的特点在于他本身没有排斥性,在能选择item的情况下,也能够响应普通点击事件。为了解决问题 ,在android3.0之后添加了CHOICE_MODE_MULTIPLE_MODAL模式。


    二、使用ListViiewCHOICE_MODE_MULTIPLE模式

    CHOICE_MODE_MULTIPLE_MODAL和CHOICE_MODE_MULTIPLE恰恰相反。他是对普通点击操作和多选操作是排斥的,一旦有一个item被选中。即进入到多选状态,item的onclick事件被屏蔽。

    这样的排斥性也是他比CHOICE_MODE_MULTIPLE多了个MODAL的原因。此外CHOICE_MODE_MULTIPLE_MODAL还结合了android3.0的actionmode,当进入多选状态,actionbar的位置会显示新的菜单。

    我们来看看CHOICE_MODE_MULTIPLE_MODAL模式的实现原理:


    怎样实现两种状态的相互排斥:当点击一个item的时候absListView中会调用performItemClick,假设是CHOICE_MODE_MULTIPLE。则该item点击一次,mCheckStates中对应位置的状态变更一次。可是CHOICE_MODE_MULTIPLE_MODAL模式不同,必需要mChoiceActionMode!= null

    的情况下,才会去变更mCheckStates中对应位置的状态。不光如此。假设mChoiceActionMode!= null

    。他还会阻挡ItemClick事件的继续传播,从而屏蔽了ListView OnItemClickListener的onItemClick方法。


    怎样启用actionmode:一般我们使用actionmode都是在activity中调用startActionMode,可是假设你要使用ListView的CHOICE_MODE_MULTIPLE_MODAL,请不要这么做。 在absListView中有一个变量mChoiceActionMode。定义例如以下:

    ActionMode mChoiceActionMode;

    当长按item 或者是调用主动调用setItemChecked方法mChoiceActionMode将被实例化,而假设你是在activity中调用startActionMode,那么尽管actionbar上的菜单变化了,ListView 中的mChoiceActionMode却没有实例化。刚刚我们谈到mChoiceActionMode==null 表示未进入到多选状态,所以这时你点击一个item事实上还是普通的点击行为。

    因此在CHOICE_MODE_MULTIPLE_MODAL模式下要启用多选操作。仅仅有两种办法:

    (1)长按当长按item 。

    (2)主动调用ListView的setItemChecked(int position, boolean value)方法选中一个item。

    可是这两种进入多选状态的方法都有一个弊端,那就是进入多选状态之后,总是有一个item是被选中的, 方法(1)中长按item。被按的item被选中,这样的结果是合理的能够接受的,可是假设你想主动进入多选状态(比方我在点击actionbar的某个菜单的时候想进入多选状态),就必须採用方法(2):调用setItemChecked,这就出现个问题。你该让哪个item被选中呢?貌似最合理的该是一个都不选中吧,我仅仅是进入到这个状态,还没有開始选呢。幸运的是,我们能够使用一些技巧,实现能主动进入多选状态。且没有一个item被选中。

    思路是我们先让第一个item被选中。这样Listview就进入多选状态。然后我们再清除被选中item的状态,代码例如以下:
    if(item.getItemId() == R.id.action_choice){
         mListView.setItemChecked(0,true);
         mListView.clearChoices();
    }

    有些人可能会问。依照上面的思路。为什么不这样实现呢:
    if(item.getItemId() == R.id.action_choice){
                                                                                                                                                                                                                                                                                                                                                                      
         mListView.setItemChecked(0,true);
         mListView.setItemChecked(0,false);
    }

    嘿嘿,刚刚我们提到ListView CHOICE_MODE_MULTIPLE_MODAL模式中,一旦有一个item被选中,即进入到多选状态,而他还有个相反的特性,一旦全部的Item被主动的设置为未选中,则退出多选状态,mChoiceActionMode会调用自己的finish()方法。为什么呢?在MultiChoiceModeWrapper类中:
    @Override
    public void onItemCheckedStateChanged(ActionMode mode,
            int position, long id, boolean checked) {
        mWrapped.onItemCheckedStateChanged(mode, position, id, checked);
        // If there are no items selected we no longer need the selection mode.
        if (getCheckedItemCount() == 0) {
            mode.finish();
        }
    }

    好了我们来实现一个CHOICE_MODE_MULTIPLE_MODAL模式下的多选操作:


    代码:

    package com.example.listmultichoise;
    import android.os.Bundle;
    import android.app.Activity;
    import android.util.Log;
    import android.view.ActionMode;
    import android.view.LayoutInflater;
    import android.view.Menu;
    import android.view.MenuInflater;
    import android.view.MenuItem;
    import android.view.View;
    import android.view.ViewGroup;
    import android.widget.AdapterView;
    import android.widget.AdapterView.OnItemClickListener;
    import android.widget.ListView;
    import android.widget.TextView;
    import android.widget.Toast;
    public class ChoiceModeMultipleModalActivity extends Activity {
        ListView mListView = null;
        MyListAdapter mAdapter;
        ModeCallback mCallback;
                                                                                                                     
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_list);
                                                                                                                         
            mListView = (ListView)findViewById(R.id.list);
            mAdapter = new MyListAdapter(this,mListView);
            mListView.setAdapter(mAdapter);
                                                                                                                         
            mCallback = new ModeCallback();
            mListView.setChoiceMode(ListView.CHOICE_MODE_MULTIPLE_MODAL); 
            mListView.setMultiChoiceModeListener(mCallback);
            mListView.setOnItemClickListener(new OnItemClickListener() {
                public void onItemClick(AdapterView<?> parent, View view, int position,
                        long id) {
                       Toast.makeText(ChoiceModeMultipleModalActivity.this, "选择了一个item", 300).show();
                }
            });
        }
        @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) {
            if(item.getItemId() == R.id.action_choice){
                //这里使用了一点技巧来实现处于选中状态 可是0个item 被选择
                mListView.setItemChecked(0,true);
                mListView.clearChoices();
                mCallback.updateSeletedCount();
            }
            return super.onOptionsItemSelected(item);
        }
                                                                                                                     
        private class ModeCallback implements ListView.MultiChoiceModeListener {
            private View mMultiSelectActionBarView;
            private TextView mSelectedCount;
            @Override
            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(ChoiceModeMultipleModalActivity.this)
                        .inflate(R.layout.list_multi_select_actionbar, null);
                    mSelectedCount =
                        (TextView)mMultiSelectActionBarView.findViewById(R.id.selected_conv_count);
                }
                mode.setCustomView(mMultiSelectActionBarView);
                ((TextView)mMultiSelectActionBarView.findViewById(R.id.title)).setText(R.string.select_item);
                return true;
            }
            @Override
            public boolean onPrepareActionMode(ActionMode mode, Menu menu) {
                if (mMultiSelectActionBarView == null) {
                    ViewGroup v = (ViewGroup)LayoutInflater.from(ChoiceModeMultipleModalActivity.this)
                        .inflate(R.layout.list_multi_select_actionbar, null);
                    mode.setCustomView(v);
                    mSelectedCount = (TextView)v.findViewById(R.id.selected_conv_count);
                }          
                //更新菜单的状态
                MenuItem mItem = menu.findItem(R.id.action_slelect);
                if(mListView.getCheckedItemCount() == mAdapter.getCount()){
                    mItem.setTitle(R.string.action_deselect_all);
                }else{
                    mItem.setTitle(R.string.action_select_all);
                }
                return true;
            }
            @Override
            public boolean onActionItemClicked(ActionMode mode, MenuItem item) {
                switch (item.getItemId()) {
                    case R.id.action_slelect:
                        if(mListView.getCheckedItemCount() == mAdapter.getCount()){
                            unSelectedAll();
                        }else{
                            selectedAll();
                        }
                        mAdapter.notifyDataSetChanged();
                        break;
                    default:
                        break;
                }
                return true;
            }
            @Override
            public void onDestroyActionMode(ActionMode mode) {
                mListView.clearChoices();
            }
            @Override
            public void onItemCheckedStateChanged(ActionMode mode,
                    int position, long id, boolean checked) {
                updateSeletedCount();
                mode.invalidate();
                mAdapter.notifyDataSetChanged();
            }
                                                                                                                         
            public void updateSeletedCount(){
                mSelectedCount.setText(Integer.toString(mListView.getCheckedItemCount()));
            }
        }
        public void selectedAll(){
            for(int i= 0; i< mAdapter.getCount(); i++){
                mListView.setItemChecked(i, true);
            }
            mCallback.updateSeletedCount();
        }
                                                                                                                     
        public void unSelectedAll(){
            mListView.clearChoices();
            mListView.setItemChecked(0,false);
            mCallback.updateSeletedCount();
        }
    }

    这里须要提醒的是尽管ListView的mActionMode我们不能直接操作,可是actionmode的回调方法是能够在activity中设置的:
    mListView.setMultiChoiceModeListener(mCallback);

    并且这个回调方法比一般的actionmode回调方法多了个onItemCheckedStateChanged
    @Override
    public void onItemCheckedStateChanged(ActionMode mode,
            int position, long id, boolean checked) {
        ....
    }

    demo我已经上传到了csdn:http://download.csdn.net/detail/jianghejie123/8126071


    版权声明:本文博客原创文章。博客,未经同意,不得转载。

  • 相关阅读:
    MongoCola Web化
    Qsys在系统集成中的应用
    js浏览器和浏览器插件检测的方法总结
    搭建一个简单的Struts2应用
    Moon.ORM最便捷轻盈的ORM
    如何从 Winform 移植到 Webform [自己搞定HTTP协议]
    细细品味Hadoop_Hadoop集群(目录)
    微软SQL Server 2012新特性Silverlight报表客户端 Power View
    json入门实例
    项目经理
  • 原文地址:https://www.cnblogs.com/mfrbuaa/p/4747628.html
Copyright © 2011-2022 走看看