ListView适配器及缓存机制

来源:互联网 发布:阿里云销售待遇 编辑:程序博客网 时间:2024/05/14 04:59
ListView和适配器的基础:
它是如何工作的:

  • ListView 向适配器说“给我的每个条目一个布局”
  • 一个新的布局创建出来并显示出来

下一个问题:当我们有10亿个条目的时候怎么办,难道新创建一个新的布局并显示出来吗?答案肯定是“不”。Android会为你把布局缓存起来。
这一部分在Android中称呼为"Recycle - 回收利用"。以下为它的具体实现过程图。
 listview_recycler.jpg 

  • 当你有一亿个条目的时候, 只有可看见的View保存在内存中+Recycle过的View
  • 当ListView第一次向适配器请求一个VIew的时候,convertView为null,因此需要新建一个convertView.
  • 当ListView请求一个条目item1的VIew,并且item1已经超出屏幕之外,并进来一个相同类型的条目从底部进入到屏幕里面,这时convertVIew 不为null,而是等于item1。 你只需要获取新的数据装载到该View里面并返回回去。而不必要重新创建一个新的VIew
下面为简单的代码实现:
  1. public class MultipleItemsList extends ListActivity {

  2.     private MyCustomAdapter mAdapter;

  3.     @Override
  4.     public void onCreate(Bundle savedInstanceState) {
  5.         super.onCreate(savedInstanceState);
  6.         mAdapter = new MyCustomAdapter();
  7.         for (int i = 0; i < 50; i++) {
  8.             mAdapter.addItem("item " + i);
  9.         }
  10.         setListAdapter(mAdapter);
  11.     }

  12.     private class MyCustomAdapter extends BaseAdapter {

  13.         private ArrayList mData = new ArrayList();
  14.         private LayoutInflater mInflater;

  15.         public MyCustomAdapter() {
  16.             mInflater = (LayoutInflater)getSystemService(Context.LAYOUT_INFLATER_SERVICE);
  17.         }

  18.         public void addItem(final String item) {
  19.             mData.add(item);
  20.             notifyDataSetChanged();
  21.         }

  22.         @Override
  23.         public int getCount() {
  24.             return mData.size();
  25.         }

  26.         @Override
  27.         public String getItem(int position) {
  28.             return mData.get(position);
  29.         }

  30.         @Override
  31.         public long getItemId(int position) {
  32.             return position;
  33.         }

  34.         @Override
  35.         public View getView(int position, View convertView, ViewGroup parent) {
  36.             System.out.println("getView " + position + " " + convertView);
  37.             ViewHolder holder = null;
  38.             if (convertView == null) {
  39.                 convertView = mInflater.inflate(R.layout.item1, null);
  40.                 holder = new ViewHolder();
  41.                 holder.textView = (TextView)convertView.findViewById(R.id.text);
  42.                 convertView.setTag(holder);
  43.             } else {
  44.                 holder = (ViewHolder)convertView.getTag();
  45.             }
  46.             holder.textView.setText(mData.get(position));
  47.             return convertView;
  48.         }

  49.     }

  50.     public static class ViewHolder {
  51.         public TextView textView;
  52.     }
  53. }
复制代码


运行程序并观察发生了什么

getView方法调用了9次。对于可以看见的VIew中,convertView一直为null。
  1. 02-05 13:47:32.559: INFO/System.out(947): getView 0 null
  2. 02-05 13:47:32.570: INFO/System.out(947): getView 1 null
  3. 02-05 13:47:32.589: INFO/System.out(947): getView 2 null
  4. 02-05 13:47:32.599: INFO/System.out(947): getView 3 null
  5. 02-05 13:47:32.619: INFO/System.out(947): getView 4 null
  6. 02-05 13:47:32.629: INFO/System.out(947): getView 5 null
  7. 02-05 13:47:32.708: INFO/System.out(947): getView 6 null
  8. 02-05 13:47:32.719: INFO/System.out(947): getView 7 null
  9. 02-05 13:47:32.729: INFO/System.out(947): getView 8 null
复制代码

拖动以下,并查看输出的状态。

  1. 02-05 14:01:31.069: INFO/System.out(947): getView 11 android.widget.LinearLayout@437447d0
  2. 02-05 14:01:31.142: INFO/System.out(947): getView 12 android.widget.LinearLayout@43744ff8
  3. 02-05 14:01:31.279: INFO/System.out(947): getView 13 android.widget.LinearLayout@43743fa8
  4. 02-05 14:01:31.350: INFO/System.out(947): getView 14 android.widget.LinearLayout@43745820
  5. 02-05 14:01:31.429: INFO/System.out(947): getView 15 android.widget.LinearLayout@43746048
  6. 02-05 14:01:31.550: INFO/System.out(947): getView 16 android.widget.LinearLayout@43746870
  7. 02-05 14:01:31.669: INFO/System.out(947): getView 17 android.widget.LinearLayout@43747098
  8. 02-05 14:01:31.839: INFO/System.out(947): getView 18 android.widget.LinearLayout@437478c0
  9. 02-05 14:03:30.900: INFO/System.out(947): getView 19 android.widget.LinearLayout@43748df0
  10. 02-05 14:03:32.069: INFO/System.out(947): getView 20 android.widget.LinearLayout@437430f8
复制代码


就像我们所想到的一样,convertView不为null.当item11超出屏幕之后,并进来item21的时候2个convertView为同一个View。


不同的列表条目的View
我们举一个更加复杂点的例子吧,我们加入一个分隔符到ListView中。
你需要做的是:
  • 重写getViewTypeCount()   ->它返回不同的View的个数
  • getItemViewType(int) -> 根据它的位置返回正确的View类型
  • Create correct convertView (depending on view item type) in getView
下面为代码:

  1. public class MultipleItemsList extends ListActivity {

  2.     private MyCustomAdapter mAdapter;

  3.     @Override
  4.     public void onCreate(Bundle savedInstanceState) {
  5.         super.onCreate(savedInstanceState);
  6.         mAdapter = new MyCustomAdapter();
  7.         for (int i = 1; i < 50; i++) {
  8.             mAdapter.addItem("item " + i);
  9.             if (i % 4 == 0) {
  10.                 mAdapter.addSeparatorItem("separator " + i);
  11.             }
  12.         }
  13.         setListAdapter(mAdapter);
  14.     }

  15.     private class MyCustomAdapter extends BaseAdapter {

  16.         private static final int TYPE_ITEM = 0;
  17.         private static final int TYPE_SEPARATOR = 1;
  18.         private static final int TYPE_MAX_COUNT = TYPE_SEPARATOR + 1;

  19.         private ArrayList mData = new ArrayList();
  20.         private LayoutInflater mInflater;

  21.         private TreeSet mSeparatorsSet = new TreeSet();

  22.         public MyCustomAdapter() {
  23.             mInflater = (LayoutInflater)getSystemService(Context.LAYOUT_INFLATER_SERVICE);
  24.         }

  25.         public void addItem(final String item) {
  26.             mData.add(item);
  27.             notifyDataSetChanged();
  28.         }

  29.         public void addSeparatorItem(final String item) {
  30.             mData.add(item);
  31.             // save separator position
  32.             mSeparatorsSet.add(mData.size() - 1);
  33.             notifyDataSetChanged();
  34.         }

  35.         @Override
  36.         public int getItemViewType(int position) {
  37.             return mSeparatorsSet.contains(position) ? TYPE_SEPARATOR : TYPE_ITEM;
  38.         }

  39.         @Override
  40.         public int getViewTypeCount() {
  41.             return TYPE_MAX_COUNT;
  42.         }

  43.         @Override
  44.         public int getCount() {
  45.             return mData.size();
  46.         }

  47.         @Override
  48.         public String getItem(int position) {
  49.             return mData.get(position);
  50.         }

  51.         @Override
  52.         public long getItemId(int position) {
  53.             return position;
  54.         }

  55.         @Override
  56.         public View getView(int position, View convertView, ViewGroup parent) {
  57.             ViewHolder holder = null;
  58.             int type = getItemViewType(position);
  59.             System.out.println("getView " + position + " " + convertView + " type = " + type);
  60.             if (convertView == null) {
  61.                 holder = new ViewHolder();
  62.                 switch (type) {
  63.                     case TYPE_ITEM:
  64.                         convertView = mInflater.inflate(R.layout.item1, null);
  65.                         holder.textView = (TextView)convertView.findViewById(R.id.text);
  66.                         break;
  67.                     case TYPE_SEPARATOR:
  68.                         convertView = mInflater.inflate(R.layout.item2, null);
  69.                         holder.textView = (TextView)convertView.findViewById(R.id.textSeparator);
  70.                         break;
  71.                 }
  72.                 convertView.setTag(holder);
  73.             } else {
  74.                 holder = (ViewHolder)convertView.getTag();
  75.             }
  76.             holder.textView.setText(mData.get(position));
  77.             return convertView;
  78.         }

  79.     }

  80.     public static class ViewHolder {
  81.         public TextView textView;
  82.     }
复制代码


我们运行并查看以下我们写的代码所输出的东西,我们会发现每隔4个条目它会出现一个分隔符。




查看输出Log,没有什么特别的对于不同的类型 convertView都为Null
  1. 02-05 15:19:03.080: INFO/System.out(1035): getView 0 null type = 0
  2. 02-05 15:19:03.112: INFO/System.out(1035): getView 1 null type = 0
  3. 02-05 15:19:03.130: INFO/System.out(1035): getView 2 null type = 0
  4. 02-05 15:19:03.141: INFO/System.out(1035): getView 3 null type = 0
  5. 02-05 15:19:03.160: INFO/System.out(1035): getView 4 null type = 1
  6. 02-05 15:19:03.170: INFO/System.out(1035): getView 5 null type = 0
  7. 02-05 15:19:03.180: INFO/System.out(1035): getView 6 null type = 0
  8. 02-05 15:19:03.190: INFO/System.out(1035): getView 7 null type = 0
  9. 02-05 15:19:03.210: INFO/System.out(1035): getView 8 null type = 0
  10. 02-05 15:19:03.210: INFO/System.out(1035): getView 9 null type = 1
复制代码


拖动一下,看看Log中有什么变化
  1. 02-05 15:19:54.160: INFO/System.out(1035): getView 10 null type = 0
  2. 02-05 15:19:57.440: INFO/System.out(1035): getView 11 android.widget.LinearLayout@43744528 type = 0
  3. 02-05 15:20:01.310: INFO/System.out(1035): getView 12 android.widget.LinearLayout@43744eb0 type = 0
  4. 02-05 15:20:01.880: INFO/System.out(1035): getView 13 android.widget.LinearLayout@437456d8 type = 0
  5. 02-05 15:20:02.869: INFO/System.out(1035): getView 14 null type = 1
  6. 02-05 15:20:06.489: INFO/System.out(1035): getView 15 android.widget.LinearLayout@43745f00 type = 0
  7. 02-05 15:20:07.749: INFO/System.out(1035): getView 16 android.widget.LinearLayout@43747170 type = 0
  8. 02-05 15:20:10.250: INFO/System.out(1035): getView 17 android.widget.LinearLayout@43747998 type = 0
  9. 02-05 15:20:11.661: INFO/System.out(1035): getView 18 android.widget.LinearLayout@437481c0 type = 0
  10. 02-05 15:20:13.180: INFO/System.out(1035): getView 19 android.widget.LinearLayout@437468a0 type = 1
  11. 02-05 15:20:16.900: INFO/System.out(1035): getView 20 android.widget.LinearLayout@437489e8 type = 0
  12. 02-05 15:20:25.690: INFO/System.out(1035): getView 21 android.widget.LinearLayout@4374a8d8 type = 0
复制代码


分隔符的convertView为Null,直到第一个分隔符看见为止。当它超出屏幕时,View还会缓存到Recycler中,以使convertView显示出来。

源码:  MultipleItemsList.zip (31.67 KB, 下载次数: 175) 
原创粉丝点击