ListView 和 Adapter 的基础
工作原理:
ListView 针对List中每个item,要求 adapter “给我一个视图” (getView)。
一个新的视图被返回并显示
如果我们有上亿个项目要显示怎么办?为每个项目创建一个新视图?NO!这不可能!
实际上Android为你缓存了视图。
Android中有个叫做Recycler的构件,下图是他的工作原理: 如果你有10亿个项目(item),其中只有可见的项目存在内存中,其他的在Recycler中。
ListView先请求一个type1视图(getView)然后请求其他可见的项目。convertView在getView中是空(null)的。
当item1滚出屏幕,并且一个新的项目从屏幕低端上来时,ListView再请求一个type1视图。convertView此时不是空值了,它的值是item1。你只需设定新的数据然后返回convertView,不必重新创建一个视图。
请看下面的示例代码,这里在getView中使用了System.out进行输出
01
public class MultipleItemsList extends ListActivity {
02

03
private MyCustomAdapter mAdapter;
04

05
@Override
06
public void onCreate(Bundle savedInstanceState) {
07
super.onCreate(savedInstanceState);
08
mAdapter = new MyCustomAdapter();
09
for (int i = 0; i < 50; i++) {
10
mAdapter.addItem("item " + i);
11
}
12
setListAdapter(mAdapter);
13
}
14

15
private class MyCustomAdapter extends BaseAdapter {
16

17
private ArrayList mData = new ArrayList();
18
private LayoutInflater mInflater;
19

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

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

29
@Override
30
public int getCount() {
31
return mData.size();
32
}
33

34
@Override
35
public String getItem(int position) {
36
return mData.get(position);
37
}
38

39
@Override
40
public long getItemId(int position) {
41
return position;
42
}
43

44
@Override
45
public View getView(int position, View convertView, ViewGroup parent) {
46
System.out.println("getView " + position + " " + convertView);
47
ViewHolder holder = null;
48
if (convertView == null) {
49
convertView = mInflater.inflate(R.layout.item1, null);
50
holder = new ViewHolder();
51
holder.textView = (TextView)convertView.findViewById(R.id.text);
52
convertView.setTag(holder);
53
} else {
54
holder = (ViewHolder)convertView.getTag();
55
}
56
holder.textView.setText(mData.get(position));
57
return convertView;
58
}
59

60
}
61

62
public static class ViewHolder {
63
public TextView textView;
64
}
65
}

执行程序,然后在Logcat中查看日志
getView 被调用 9 次 ,convertView 对于所有的可见项目是空值(如下)

02-05 13:47:32.559: INFO/System.out(947): getView 0 null
02-05 13:47:32.570: INFO/System.out(947): getView 1 null
02-05 13:47:32.589: INFO/System.out(947): getView 2 null
02-05 13:47:32.599: INFO/System.out(947): getView 3 null
02-05 13:47:32.619: INFO/System.out(947): getView 4 null
02-05 13:47:32.629: INFO/System.out(947): getView 5 null
02-05 13:47:32.708: INFO/System.out(947): getView 6 null
02-05 13:47:32.719: INFO/System.out(947): getView 7 null
02-05 13:47:32.729: INFO/System.out(947): getView 8 null

然后稍微向下滚动List,直到item10出现:
convertView仍然是空值,因为recycler中没有视图(item1的边缘仍然可见,在顶端)

02-05 13:48:25.169: INFO/System.out(947): getView 9 null

再滚动List
convertView不是空值了!item1离开屏幕到Recycler中去了,然后item11被创建

02-05 13:48:42.879: INFO/System.out(947): getView 10 android.widget.LinearLayout@437430f8

再滚动:
02-05 14:01:31.069: INFO/System.out(947): getView 11 android.widget.LinearLayout@437447d0
02-05 14:01:31.142: INFO/System.out(947): getView 12 android.widget.LinearLayout@43744ff8
02-05 14:01:31.279: INFO/System.out(947): getView 13 android.widget.LinearLayout@43743fa8
02-05 14:01:31.350: INFO/System.out(947): getView 14 android.widget.LinearLayout@43745820
02-05 14:01:31.429: INFO/System.out(947): getView 15 android.widget.LinearLayout@43746048
02-05 14:01:31.550: INFO/System.out(947): getView 16 android.widget.LinearLayout@43746870
02-05 14:01:31.669: INFO/System.out(947): getView 17 android.widget.LinearLayout@43747098
02-05 14:01:31.839: INFO/System.out(947): getView 18 android.widget.LinearLayout@437478c0
02-05 14:03:30.900: INFO/System.out(947): getView 19 android.widget.LinearLayout@43748df0
02-05 14:03:32.069: INFO/System.out(947): getView 20 android.widget.LinearLayout@437430f8
convertView 如我们所期待的非空了,在item11离开屏幕之后,它的视图(@437430f8)作为convertView容纳item21了
不同的项目布局(item layout)
我们再举一个稍微复杂的例子,在上例的list中加入一些分隔线
你需要做这些:
重(@Override)写 getViewTypeCount() �C 返回你有多少个不同的布局
重写 getItemViewType(int) �C 由position返回view type id
根据view item的类型,在getView中创建正确的convertView
以下是代码:

001
public class MultipleItemsList extends ListActivity {
002

003
private MyCustomAdapter mAdapter;
004

005
@Override
006
public void onCreate(Bundle savedInstanceState) {
007
super.onCreate(savedInstanceState);
008
mAdapter = new MyCustomAdapter();
009
for (int i = 1; i < 50; i++) {
010
mAdapter.addItem("item " + i);
011
if (i % 4 == 0) {
012
mAdapter.addSeparatorItem("separator " + i);
013
}
014
}
015
setListAdapter(mAdapter);
016
}
017

018
private class MyCustomAdapter extends BaseAdapter {
019

020
private static final int TYPE_ITEM = 0;
021
private static final int TYPE_SEPARATOR = 1;
022
private static final int TYPE_MAX_COUNT = TYPE_SEPARATOR + 1;
023

024
private ArrayList mData = new ArrayList();
025
private LayoutInflater mInflater;
026

027
private TreeSet mSeparatorsSet = new TreeSet();
028

029
public MyCustomAdapter() {
030
mInflater = (LayoutInflater)getSystemService(Context.LAYOUT_INFLATER_SERVICE);
031
}
032

033
public void addItem(final String item) {
034
mData.add(item);
035
notifyDataSetChanged();
036
}
037

038
public void addSeparatorItem(final String item) {
039
mData.add(item);
040
// save separator position
041
mSeparatorsSet.add(mData.size() - 1);
042
notifyDataSetChanged();
043
}
044

045
@Override
046
public int getItemViewType(int position) {
047
return mSeparatorsSet.contains(position) ? TYPE_SEPARATOR : TYPE_ITEM;
048
}
049

050
@Override
051
public int getViewTypeCount() {
052
return TYPE_MAX_COUNT;
053
}
054

055
@Override
056
public int getCount() {
057
return mData.size();
058
}
059

060
@Override
061
public String getItem(int position) {
062
return mData.get(position);
063
}
064

065
@Override
066
public long getItemId(int position) {
067
return position;
068
}
069

070
@Override
071
public View getView(int position, View convertView, ViewGroup parent) {
072
ViewHolder holder = null;
073
int type = getItemViewType(position);
074
System.out.println("getView " + position + " " + convertView + " type = " + type);
075
if (convertView == null) {
076
holder = new ViewHolder();
077
switch (type) {
078
case TYPE_ITEM:
079
convertView = mInflater.inflate(R.layout.item1, null);
080
holder.textView = (TextView)convertView.findViewById(R.id.text);
081
break;
082
case TYPE_SEPARATOR:
083
convertView = mInflater.inflate(R.layout.item2, null);
084
holder.textView = (TextView)convertView.findViewById(R.id.textSeparator);
085
break;
086
}
087
convertView.setTag(holder);
088
} else {
089
holder = (ViewHolder)convertView.getTag();
090
}
091
holder.textView.setText(mData.get(position));
092
return convertView;
093
}
094

095
}
096

097
public static class ViewHolder {
098
public TextView textView;
099
}
100
}

运行程序,你会看到每4个item一个分割线 看看日志,无异常,所有的convertView都是空的

02-05 15:19:03.080: INFO/System.out(1035): getView 0 null type = 0
02-05 15:19:03.112: INFO/System.out(1035): getView 1 null type = 0
02-05 15:19:03.130: INFO/System.out(1035): getView 2 null type = 0
02-05 15:19:03.141: INFO/System.out(1035): getView 3 null type = 0
02-05 15:19:03.160: INFO/System.out(1035): getView 4 null type = 1
02-05 15:19:03.170: INFO/System.out(1035): getView 5 null type = 0
02-05 15:19:03.180: INFO/System.out(1035): getView 6 null type = 0
02-05 15:19:03.190: INFO/System.out(1035): getView 7 null type = 0
02-05 15:19:03.210: INFO/System.out(1035): getView 8 null type = 0
02-05 15:19:03.210: INFO/System.out(1035): getView 9 null type = 1
滚动list:
02-05 15:19:54.160: INFO/System.out(1035): getView 10 null type = 0
02-05 15:19:57.440: INFO/System.out(1035): getView 11 android.widget.LinearLayout@43744528 type = 0
02-05 15:20:01.310: INFO/System.out(1035): getView 12 android.widget.LinearLayout@43744eb0 type = 0
02-05 15:20:01.880: INFO/System.out(1035): getView 13 android.widget.LinearLayout@437456d8 type = 0
02-05 15:20:02.869: INFO/System.out(1035): getView 14 null type = 1
02-05 15:20:06.489: INFO/System.out(1035): getView 15 android.widget.LinearLayout@43745f00 type = 0
02-05 15:20:07.749: INFO/System.out(1035): getView 16 android.widget.LinearLayout@43747170 type = 0
02-05 15:20:10.250: INFO/System.out(1035): getView 17 android.widget.LinearLayout@43747998 type = 0
02-05 15:20:11.661: INFO/System.out(1035): getView 18 android.widget.LinearLayout@437481c0 type = 0
02-05 15:20:13.180: INFO/System.out(1035): getView 19 android.widget.LinearLayout@437468a0 type = 1
02-05 15:20:16.900: INFO/System.out(1035): getView 20 android.widget.LinearLayout@437489e8 type = 0
02-05 15:20:25.690: INFO/System.out(1035): getView 21 android.widget.LinearLayout@4374a8d8 type = 0
convertView对于分割线是空的,直到第一个分割线可见,当其离开屏幕,视图去到Recycler并且convertView开始起作用。

更多相关文章

  1. 没有一行代码,「2020 新冠肺炎记忆」这个项目却登上了 GitHub 中
  2. 不吹不黑!GitHub 上帮助人们学习编码的 12 个资源,错过血亏...
  3. 10.1 Android(安卓)NDK开发 一
  4. Android(安卓)studio 统一版本依赖, 避免build不同依赖版本冲突
  5. Google官方教程:Android平板界面适配之屏幕尺寸限定符
  6. android ImageView 的几点经验总结
  7. Android(安卓)Studio系列教程(二)
  8. 在Android(安卓)Studio 2.3 中开发运行 Cordova 8.0.0项目
  9. Android学习系列之(二)项目目录结构基本介绍

随机推荐

  1. Android(安卓)系统Handler用法简介
  2. android studio 试手
  3. android开发――显式intent
  4. Android中ViewGroup到View的Touch事件的
  5. Android(安卓)多平台解决方案
  6. My Magic Android(安卓)Tour —— 处女作
  7. Android中Settings.System的使用
  8. android移动支付——PayPal支付
  9. Android(安卓)定位服务
  10. Android推送通知的实现--采用MQTT协议实