• 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的多选模式有点愚蠢。

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

    实现的大致框架如下:

    Adapter中:

    保存被选择的position

    [html] view plain copy
     
     在CODE上查看代码片派生到我的代码片
    1. public HashSet<LongselectedItems = new HashSet<Long>();  


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

    [html] view plain copy
     
     在CODE上查看代码片派生到我的代码片
    1.     public View getView(int position, View convertView, ViewGroup par) {  
    2. ......  
    3.             if(selectedItems.contains((long)position)){  
    4.                 holder.cBox.setChecked(true);  
    5.             }else{  
    6.                 holder.cBox.setChecked(false);  
    7.             }  
    8.             if(selectedMode==AppContext.MULTI_SELECTED){  
    9.                 holder.cBox.setVisibility(View.VISIBLE);  
    10.                 holder.check_box_wraper.setVisibility(View.VISIBLE);  
    11.             }else{  
    12.                 holder.cBox.setVisibility(View.GONE);  
    13.                 holder.check_box_wraper.setVisibility(View.GONE);  
    14.             }  
    15. .....  
    16. }  

    Activity中:

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

    [html] view plain copy
     
     在CODE上查看代码片派生到我的代码片
    1. @Override  
    2. public void onItemClick(AdapterView<?> a, View v, int position, long id) {  
    3.     //普通模式 :直接打开一个activity  
    4.         if(itemClickActionMode==AppContext.VIEW_NOTE){  
    5.             Long mId=Long.parseLong(idText.getText().toString());  
    6.             Uri uri = ContentUris.withAppendedId(getIntent().getData(), mId);  
    7.             startActivity(new Intent(Intent.ACTION_VIEW, uri));  
    8.         }  
    9.         //多选模式:更新adapter中selectedItems 集合的值,同时 让adapter在getView中改变item的外观。  
    10.        else{  
    11.             ViewHolder vHollder = (ViewHolder) v.getTag();  
    12.             if(mAdapter.selectedItems.contains((long)position)){                                                                                                                                                    mAdapter.selectedItems.remove((long)position);  
    13.             }else{  
    14.                         mAdapter.selectedItems.add((long)position);  
    15.             }  
    16.             mAdapter.notifyDataSetChanged();  
    17.             onItemSelected(getSelectedCount());        
    18.         }   
    19.     }  

    上面的做法其实用的很普遍。但是我们不提倡。

    二、使用ListViiew的CHOICE_MODE_MULTIPLE模式

    ListView有四种模式:

    [html] view plain copy
     
     在CODE上查看代码片派生到我的代码片
    1. /**  
    2.  * Normal list that does not indicate choices  
    3.  */  
    4. public static final int CHOICE_MODE_NONE = 0;  
    5. /**  
    6.  * The list allows up to one choice  
    7.  */  
    8. public static final int CHOICE_MODE_SINGLE = 1;  
    9. /**  
    10.  * The list allows multiple choices  
    11.  */  
    12. public static final int CHOICE_MODE_MULTIPLE = 2;  
    13. /**  
    14.  * The list allows multiple choices in a modal selection mode  
    15.  */  
    16. 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实战

     

    先看看效果图

    [html] view plain copy
     
     在CODE上查看代码片派生到我的代码片
    1. package com.example.listmultichoise;  
    2. import android.os.Bundle;  
    3. import android.app.ActionBar;  
    4. import android.app.Activity;  
    5. import android.util.Log;  
    6. import android.view.ActionMode;  
    7. import android.view.LayoutInflater;  
    8. import android.view.Menu;  
    9. import android.view.MenuInflater;  
    10. import android.view.MenuItem;  
    11. import android.view.View;  
    12. import android.view.ViewGroup;  
    13. import android.view.Window;  
    14. import android.widget.AdapterView;  
    15. import android.widget.AdapterView.OnItemClickListener;  
    16. import android.widget.ListView;  
    17. import android.widget.TextView;  
    18. import android.widget.Toast;  
    19. public class ChoiceModeMultipleActivity extends Activity {  
    20.     ListView mListView = null;  
    21.     MyListAdapter mAdapter;  
    22.     private View mMultiSelectActionBarView;  
    23.     private TextView mSelectedCount;  
    24.   @Override  
    25.     protected void onCreate(Bundle savedInstanceState) {  
    26.         super.onCreate(savedInstanceState);  
    27.         getWindow().requestFeature(Window.FEATURE_ACTION_BAR);  
    28.         setContentView(R.layout.activity_list);  
    29.        
    30.         mListView = (ListView)findViewById(R.id.list);  
    31.         mAdapter = new MyListAdapter(this,mListView);  
    32.         mListView.setAdapter(mAdapter);  
    33.         mListView.setChoiceMode(ListView.CHOICE_MODE_MULTIPLE);  
    34.         mListView.setOnItemClickListener(new OnItemClickListener() {  
    35.             public void onItemClick(AdapterView<?> parent, View view, int position,  
    36.                     long id) {  
    37.                 mAdapter.notifyDataSetChanged();  
    38.                 updateSeletedCount();  
    39.             }  
    40.         });  
    41.         
    42.         if (mMultiSelectActionBarView == null) {  
    43.             mMultiSelectActionBarView = LayoutInflater.from(ChoiceModeMultipleActivity.this)  
    44.                 .inflate(R.layout.list_multi_select_actionbar, null);  
    45.             mSelectedCount =  
    46.                 (TextView)mMultiSelectActionBarView.findViewById(R.id.selected_conv_count);  
    47.         }  
    48.         getActionBar().setDisplayOptions(ActionBar.DISPLAY_SHOW_CUSTOM,  
    49.                 ActionBar.DISPLAY_SHOW_CUSTOM | ActionBar.DISPLAY_SHOW_HOME |  
    50.                 ActionBar.DISPLAY_SHOW_TITLE);  
    51.         getActionBar().setCustomView(mMultiSelectActionBarView);  
    52.         ((TextView)mMultiSelectActionBarView.findViewById(R.id.title)).setText(R.string.select_item);  
    53.     }  
    54.     @Override  
    55.     public boolean onCreateOptionsMenu(Menu menu) {  
    56.  
    57.         getMenuInflater().inflate(R.menu.multi_select_menu, menu);  
    58.         return true;  
    59.     }  
    60.     @Override  
    61.     public boolean onPrepareOptionsMenu(Menu menu) {                                                                                                                                              MenuItem mItem = menu.findItem(R.id.action_slelect);  
    62.         if(mListView.getCheckedItemCount() == mAdapter.getCount()){  
    63.             mItem.setTitle(R.string.action_deselect_all);  
    64.         }else{  
    65.             mItem.setTitle(R.string.action_select_all);  
    66.         }   
    67.         return super.onPrepareOptionsMenu(menu);  
    68.     }  
    69.         
    70.     @Override  
    71.     public boolean onOptionsItemSelected(MenuItem item) {  
    72.         switch (item.getItemId()) {  
    73.         case R.id.action_slelect:  
    74.             if(mListView.getCheckedItemCount() == mAdapter.getCount()){  
    75.                 unSelectedAll();  
    76.             }else{  
    77.                 selectedAll();  
    78.             }  
    79.             mAdapter.notifyDataSetChanged();  
    80.             break;  
    81.         default:  
    82.             break;  
    83.         }  
    84.         return super.onOptionsItemSelected(item);  
    85.     }  
    86.   public void selectedAll(){  
    87.         for(int i= 0; imAdapter.getCount(); i++){  
    88.             mListView.setItemChecked(i, true);  
    89.         }  
    90.         updateSeletedCount();  
    91.     }  
    92.  public void unSelectedAll(){  
    93.         mListView.clearChoices();  
    94.         updateSeletedCount();  
    95.     }  
    96.  public void updateSeletedCount(){  
    97.         mSelectedCount.setText(Integer.toString(mListView.getCheckedItemCount()));  
    98.     }  
    99. }  

    代码解释:

     

    首先设置ListView模式:

    [html] view plain copy
     
     在CODE上查看代码片派生到我的代码片
    1. mListView.setChoiceMode(ListView.CHOICE_MODE_MULTIPLE);  

    定义一个adapter,当ListView的某个item被选中之后,将该Item的背景设置为蓝色,以标记为选中。不然虽然ListView知道该item被选中,但是界面上没表现出来。

    [html] view plain copy
     
     在CODE上查看代码片派生到我的代码片
    1. ......  
    2.     public View getView(int position, View convertView, ViewGroup parent) {  
    3.         TextView tv;  
    4.         if (convertView == null) {  
    5.             tv = (TextView) LayoutInflater.from(mContext).inflate(  
    6.                     android.R.layout.simple_expandable_list_item_1, parent,  
    7.                     false);  
    8.         } else {  
    9.             tv = (TextView) convertView;  
    10.         }  
    11.         tv.setText(mStrings[position]);  
    12.         updateBackground(position , tv);  
    13.         return tv;  
    14.     }  
    15.     @SuppressLint("NewApi")  
    16.     public void updateBackground(int position, View view) {  
    17.         int backgroundId;  
    18.         if (mListView.isItemChecked(position)) {  
    19.             backgroundId = R.drawable.list_selected_holo_light;  
    20.         } else {  
    21.             backgroundId = R.drawable.conversation_item_background_read;  
    22.         }  
    23.         Drawable background = mContext.getResources().getDrawable(backgroundId);  
    24.         view.setBackground(background);  
    25.     }  
    26. ......  


    在item每被点击一次中通知adapter,这样做的目的是为了让更新Ui以显示最新的选中状态。

    [html] view plain copy
     
     在CODE上查看代码片派生到我的代码片
    1. mListView.setOnItemClickListener(new OnItemClickListener() {  
    2.     public void onItemClick(AdapterView<?> parent, View view, int position,  
    3.             long id) {  
    4.         mAdapter.notifyDataSetChanged();  
    5.         updateSeletedCount();  
    6.     }  
    7. });  

    其中mSelectedCount()作用是在actionbar中更新选中的数目。

    [html] view plain copy
     
     在CODE上查看代码片派生到我的代码片
    1. public void updateSeletedCount(){  
    2.        mSelectedCount.setText(Integer.toString(mListView.getCheckedItemCount()));  
    3.    }  

    上面的代码实现了多选操作,但是在我选中一个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模式。

    二、使用ListViiew的CHOICE_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,定义如下:

    [html] view plain copy
     
     在CODE上查看代码片派生到我的代码片
    1. 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的状态,代码如下:

    [html] view plain copy
     
     在CODE上查看代码片派生到我的代码片
    1. if(item.getItemId() == R.id.action_choice){  
    2.      mListView.setItemChecked(0,true);  
    3.      mListView.clearChoices();  
    4. }  


    有些人可能会问,按照上面的思路,为什么不这样实现呢:

    [html] view plain copy
     
     在CODE上查看代码片派生到我的代码片
    1. if(item.getItemId() == R.id.action_choice){  
    2.                                                                                                                                                                                                                                                                                                                                                                     
    3.      mListView.setItemChecked(0,true);  
    4.      mListView.setItemChecked(0,false);  
    5. }  


    嘿嘿,刚刚我们提到ListView CHOICE_MODE_MULTIPLE_MODAL模式中,一旦有一个item被选中,即进入到多选状态,而他还有个相反的特性,一旦所有的Item被主动的设置为未选中,则退出多选状态,mChoiceActionMode会调用自己的finish()方法。为什么呢?在MultiChoiceModeWrapper类中:

    [html] view plain copy
     
     在CODE上查看代码片派生到我的代码片
    1. @Override  
    2. public void onItemCheckedStateChanged(ActionMode mode,  
    3.         int position, long id, boolean checked) {  
    4.     mWrapped.onItemCheckedStateChanged(mode, position, id, checked);  
    5.     // If there are no items selected we no longer need the selection mode.  
    6.     if (getCheckedItemCount() == 0) {  
    7.         mode.finish();  
    8.     }  
    9. }  


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

    代码:

    [html] view plain copy
     
     在CODE上查看代码片派生到我的代码片
    1. package com.example.listmultichoise;  
    2. import android.os.Bundle;  
    3. import android.app.Activity;  
    4. import android.util.Log;  
    5. import android.view.ActionMode;  
    6. import android.view.LayoutInflater;  
    7. import android.view.Menu;  
    8. import android.view.MenuInflater;  
    9. import android.view.MenuItem;  
    10. import android.view.View;  
    11. import android.view.ViewGroup;  
    12. import android.widget.AdapterView;  
    13. import android.widget.AdapterView.OnItemClickListener;  
    14. import android.widget.ListView;  
    15. import android.widget.TextView;  
    16. import android.widget.Toast;  
    17. public class ChoiceModeMultipleModalActivity extends Activity {  
    18.     ListView mListView = null;  
    19.     MyListAdapter mAdapter;  
    20.     ModeCallback mCallback;  
    21.                                                                                                                    
    22.     @Override  
    23.     protected void onCreate(Bundle savedInstanceState) {  
    24.         super.onCreate(savedInstanceState);  
    25.         setContentView(R.layout.activity_list);  
    26.                                                                                                                        
    27.         mListView = (ListView)findViewById(R.id.list);  
    28.         mAdapter = new MyListAdapter(this,mListView);  
    29.         mListView.setAdapter(mAdapter);  
    30.                                                                                                                        
    31.         mCallback = new ModeCallback();  
    32.         mListView.setChoiceMode(ListView.CHOICE_MODE_MULTIPLE_MODAL);   
    33.         mListView.setMultiChoiceModeListener(mCallback);  
    34.         mListView.setOnItemClickListener(new OnItemClickListener() {  
    35.             public void onItemClick(AdapterView<?> parent, View view, int position,  
    36.                     long id) {  
    37.                    Toast.makeText(ChoiceModeMultipleModalActivity.this, "选择了一个item", 300).show();  
    38.             }  
    39.         });  
    40.     }  
    41.     @Override  
    42.     public boolean onCreateOptionsMenu(Menu menu) {  
    43.         // Inflate the menu; this adds items to the action bar if it is present.  
    44.         getMenuInflater().inflate(R.menu.main, menu);  
    45.         return true;  
    46.     }  
    47.     @Override  
    48.     public boolean onOptionsItemSelected(MenuItem item) {  
    49.         if(item.getItemId() == R.id.action_choice){  
    50.             //这里使用了一点技巧来实现处于选中状态 但是0个item 被选择  
    51.             mListView.setItemChecked(0,true);  
    52.             mListView.clearChoices();  
    53.             mCallback.updateSeletedCount();  
    54.         }  
    55.         return super.onOptionsItemSelected(item);  
    56.     }  
    57.                                                                                                                    
    58.     private class ModeCallback implements ListView.MultiChoiceModeListener {  
    59.         private View mMultiSelectActionBarView;  
    60.         private TextView mSelectedCount;  
    61.         @Override  
    62.         public boolean onCreateActionMode(ActionMode mode, Menu menu) {  
    63.             // actionmode的菜单处理  
    64.             MenuInflater inflater = getMenuInflater();  
    65.             inflater.inflate(R.menu.multi_select_menu, menu);  
    66.             if (mMultiSelectActionBarView == null) {  
    67.                 mMultiSelectActionBarView = LayoutInflater.from(ChoiceModeMultipleModalActivity.this)  
    68.                     .inflate(R.layout.list_multi_select_actionbar, null);  
    69.                 mSelectedCount =  
    70.                     (TextView)mMultiSelectActionBarView.findViewById(R.id.selected_conv_count);  
    71.             }  
    72.             mode.setCustomView(mMultiSelectActionBarView);  
    73.             ((TextView)mMultiSelectActionBarView.findViewById(R.id.title)).setText(R.string.select_item);  
    74.             return true;  
    75.         }  
    76.         @Override  
    77.         public boolean onPrepareActionMode(ActionMode mode, Menu menu) {  
    78.             if (mMultiSelectActionBarView == null) {  
    79.                 ViewGroup v = (ViewGroup)LayoutInflater.from(ChoiceModeMultipleModalActivity.this)  
    80.                     .inflate(R.layout.list_multi_select_actionbar, null);  
    81.                 mode.setCustomView(v);  
    82.                 mSelectedCount = (TextView)v.findViewById(R.id.selected_conv_count);  
    83.             }            
    84.             //更新菜单的状态  
    85.             MenuItem mItem = menu.findItem(R.id.action_slelect);  
    86.             if(mListView.getCheckedItemCount() == mAdapter.getCount()){  
    87.                 mItem.setTitle(R.string.action_deselect_all);  
    88.             }else{  
    89.                 mItem.setTitle(R.string.action_select_all);  
    90.             }  
    91.             return true;  
    92.         }  
    93.         @Override  
    94.         public boolean onActionItemClicked(ActionMode mode, MenuItem item) {  
    95.             switch (item.getItemId()) {  
    96.                 case R.id.action_slelect:  
    97.                     if(mListView.getCheckedItemCount() == mAdapter.getCount()){  
    98.                         unSelectedAll();  
    99.                     }else{  
    100.                         selectedAll();  
    101.                     }  
    102.                     mAdapter.notifyDataSetChanged();  
    103.                     break;  
    104.                 default:  
    105.                     break;  
    106.             }  
    107.             return true;  
    108.         }  
    109.         @Override  
    110.         public void onDestroyActionMode(ActionMode mode) {  
    111.             mListView.clearChoices();  
    112.         }  
    113.         @Override  
    114.         public void onItemCheckedStateChanged(ActionMode mode,  
    115.                 int position, long id, boolean checked) {  
    116.             updateSeletedCount();  
    117.             mode.invalidate();  
    118.             mAdapter.notifyDataSetChanged();  
    119.         }  
    120.                                                                                                                        
    121.         public void updateSeletedCount(){  
    122.             mSelectedCount.setText(Integer.toString(mListView.getCheckedItemCount()));  
    123.         }  
    124.     }  
    125.     public void selectedAll(){  
    126.         for(int i= 0; imAdapter.getCount(); i++){  
    127.             mListView.setItemChecked(i, true);  
    128.         }  
    129.         mCallback.updateSeletedCount();  
    130.     }  
    131.                                                                                                                    
    132.     public void unSelectedAll(){  
    133.         mListView.clearChoices();  
    134.         mListView.setItemChecked(0,false);  
    135.         mCallback.updateSeletedCount();  
    136.     }  
    137. }  


    这里需要提醒的是虽然ListView的mActionMode我们不能直接操作,但是actionmode的回调方法是可以在activity中设置的:

    [html] view plain copy
     
     在CODE上查看代码片派生到我的代码片
    1. mListView.setMultiChoiceModeListener(mCallback);  


    而且这个回调方法比一般的actionmode回调方法多了个onItemCheckedStateChanged

    [html] view plain copy
     
     在CODE上查看代码片派生到我的代码片
    1. @Override  
    2. public void onItemCheckedStateChanged(ActionMode mode,  
    3.         int position, long id, boolean checked) {  
    4.     ....  
    5. }  


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

  • 相关阅读:
    Block为什么使用Copy?
    iOS运行时,如何增加成员变量
    安卓长按交互onCreateContextMenu的简单 用法
    iOS 检查版本号的代码
    git的基本使用
    svn的使用
    const 关键字及作用
    常见的内存分配
    保存字符串的方法
    指针的总结一(指针的定义)
  • 原文地址:https://www.cnblogs.com/jiangbeixiaoqiao/p/5213002.html
Copyright © 2020-2023  润新知