ListView 和 Adapter 的基础
工作原理:
如果我们有上亿个项目要显示怎么办?为每个项目创建一个新视图?NO!这不可能!
实际上Android为你缓存了视图。
Android中有个叫做Recycler的构件,下图是他的工作原理:
请看下面的示例代码,这里在getView中使用了System.out进行输出
public class MultipleItemsList extends ListActivity { private MyCustomAdapter mAdapter; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); mAdapter = new MyCustomAdapter(); for (int i = 0; i < 50; i++) { mAdapter.addItem("item " + i); } setListAdapter(mAdapter); } private class MyCustomAdapter extends BaseAdapter { private ArrayList mData = new ArrayList(); private LayoutInflater mInflater; public MyCustomAdapter() { mInflater = (LayoutInflater)getSystemService(Context.LAYOUT_INFLATER_SERVICE); } public void addItem(final String item) { mData.add(item); notifyDataSetChanged(); } @Override public int getCount() { return mData.size(); } @Override public String getItem(int position) { return mData.get(position); } @Override public long getItemId(int position) { return position; } @Override public View getView(int position, View convertView, ViewGroup parent) { System.out.println("getView " + position + " " + convertView); ViewHolder holder = null; if (convertView == null) { convertView = mInflater.inflate(R.layout.item1, null); holder = new ViewHolder(); holder.textView = (TextView)convertView.findViewById(R.id.text); convertView.setTag(holder); } else { holder = (ViewHolder)convertView.getTag(); } holder.textView.setText(mData.get(position)); return convertView; } } public static class ViewHolder { public TextView textView; } }
执行程序,然后在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了
我们再举一个稍微复杂的例子,在上例的list中加入一些分隔线
你需要做这些:
以下是代码:
public class MultipleItemsList extends ListActivity { private MyCustomAdapter mAdapter; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); mAdapter = new MyCustomAdapter(); for (int i = 1; i < 50; i++) { mAdapter.addItem("item " + i); if (i % 4 == 0) { mAdapter.addSeparatorItem("separator " + i); } } setListAdapter(mAdapter); } private class MyCustomAdapter extends BaseAdapter { private static final int TYPE_ITEM = 0; private static final int TYPE_SEPARATOR = 1; private static final int TYPE_MAX_COUNT = TYPE_SEPARATOR + 1; private ArrayList mData = new ArrayList(); private LayoutInflater mInflater; private TreeSet mSeparatorsSet = new TreeSet(); public MyCustomAdapter() { mInflater = (LayoutInflater)getSystemService(Context.LAYOUT_INFLATER_SERVICE); } public void addItem(final String item) { mData.add(item); notifyDataSetChanged(); } public void addSeparatorItem(final String item) { mData.add(item); // save separator position mSeparatorsSet.add(mData.size() - 1); notifyDataSetChanged(); } @Override public int getItemViewType(int position) { return mSeparatorsSet.contains(position) ? TYPE_SEPARATOR : TYPE_ITEM; } @Override public int getViewTypeCount() { return TYPE_MAX_COUNT; } @Override public int getCount() { return mData.size(); } @Override public String getItem(int position) { return mData.get(position); } @Override public long getItemId(int position) { return position; } @Override public View getView(int position, View convertView, ViewGroup parent) { ViewHolder holder = null; int type = getItemViewType(position); System.out.println("getView " + position + " " + convertView + " type = " + type); if (convertView == null) { holder = new ViewHolder(); switch (type) { case TYPE_ITEM: convertView = mInflater.inflate(R.layout.item1, null); holder.textView = (TextView)convertView.findViewById(R.id.text); break; case TYPE_SEPARATOR: convertView = mInflater.inflate(R.layout.item2, null); holder.textView = (TextView)convertView.findViewById(R.id.textSeparator); break; } convertView.setTag(holder); } else { holder = (ViewHolder)convertView.getTag(); } holder.textView.setText(mData.get(position)); return convertView; } } public static class ViewHolder { public TextView textView; } }
运行程序,你会看到每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开始起作用。
本文翻译自http://android.amberfog.com/?p=296
代码下载:MultipleItemsList.zip – source code