ListView中getView的原理+如何在ListView中放置多个item
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进行输出
| 
1 
2 
3 
4 
5 
6 
7 
8 
9 
10 
11 
12 
13 
14 
15 
16 
17 
18 
19 
20 
21 
22 
23 
24 
25 
26 
27 
28 
29 
30 
31 
32 
33 
34 
35 
36 
37 
38 
39 
40 
41 
42 
43 
44 
45 
46 
47 
48 
49 
50 
51 
52 
53 
54 
55 
56 
57 
58 
59 
60 
61 
62 
63 
64 
65 | publicclassMultipleItemsList
extendsListActivity
 {     privateMyCustomAdapter
 mAdapter;     @Override    publicvoidonCreate(Bundle
 savedInstanceState) {        super.onCreate(savedInstanceState);        mAdapter
 = newMyCustomAdapter();        for(inti
 = 0;
 i < 50;
 i++) {            mAdapter.addItem("item
 "+
 i);        }        setListAdapter(mAdapter);    }     privateclassMyCustomAdapter
extendsBaseAdapter
 {         privateArrayList
 mData = newArrayList();        privateLayoutInflater
 mInflater;         publicMyCustomAdapter()
 {            mInflater
 = (LayoutInflater)getSystemService(Context.LAYOUT_INFLATER_SERVICE);        }         publicvoidaddItem(finalString
 item) {            mData.add(item);            notifyDataSetChanged();        }         @Override        publicintgetCount()
 {            returnmData.size();        }         @Override        publicString
 getItem(intposition)
 {            returnmData.get(position);        }         @Override        publiclonggetItemId(intposition)
 {            returnposition;        }         @Override        publicView
 getView(intposition,
 View convertView, ViewGroup parent) {            System.out.println("getView
 "+
 position + "
 "+
 convertView);            ViewHolder
 holder = null;            if(convertView
 == null)
 {                convertView
 = mInflater.inflate(R.layout.item1, null);                holder
 = newViewHolder();                holder.textView
 = (TextView)convertView.findViewById(R.id.text);                convertView.setTag(holder);            }
else{                holder
 = (ViewHolder)convertView.getTag();            }            holder.textView.setText(mData.get(position));            returnconvertView;        }     }     publicstaticclassViewHolder
 {        publicTextView
 textView;    }} | 
执行程序,然后在Logcat中查看日志

getView 被调用 9 次 ,convertView 对于所有的可见项目是空值(如下)
| 02-0513:47:32.559:
 INFO/System.out(947):
 getView 0null02-0513:47:32.570:
 INFO/System.out(947):
 getView 1null02-0513:47:32.589:
 INFO/System.out(947):
 getView 2null02-0513:47:32.599:
 INFO/System.out(947):
 getView 3null02-0513:47:32.619:
 INFO/System.out(947):
 getView 4null02-0513:47:32.629:
 INFO/System.out(947):
 getView 5null02-0513:47:32.708:
 INFO/System.out(947):
 getView 6null02-0513:47:32.719:
 INFO/System.out(947):
 getView 7null02-0513:47:32.729:
 INFO/System.out(947):
 getView 8null | 
然后稍微向下滚动List,直到item10出现:

convertView仍然是空值,因为recycler中没有视图(item1的边缘仍然可见,在顶端)
| 02-0513:48:25.169:
 INFO/System.out(947):
 getView 9null | 
再滚动List

convertView不是空值了!item1离开屏幕到Recycler中去了,然后item11被创建
| 02-0513:48:42.879:
 INFO/System.out(947):
 getView 10android.widget.LinearLayout@437430f8 | 
再滚动:
| 02-0514:01:31.069:
 INFO/System.out(947):
 getView 11android.widget.LinearLayout@437447d002-0514:01:31.142:
 INFO/System.out(947):
 getView 12android.widget.LinearLayout@43744ff802-0514:01:31.279:
 INFO/System.out(947):
 getView 13android.widget.LinearLayout@43743fa802-0514:01:31.350:
 INFO/System.out(947):
 getView 14android.widget.LinearLayout@4374582002-0514:01:31.429:
 INFO/System.out(947):
 getView 15android.widget.LinearLayout@4374604802-0514:01:31.550:
 INFO/System.out(947):
 getView 16android.widget.LinearLayout@4374687002-0514:01:31.669:
 INFO/System.out(947):
 getView 17android.widget.LinearLayout@4374709802-0514:01:31.839:
 INFO/System.out(947):
 getView 18android.widget.LinearLayout@437478c002-0514:03:30.900:
 INFO/System.out(947):
 getView 19android.widget.LinearLayout@43748df002-0514:03:32.069:
 INFO/System.out(947):
 getView 20android.widget.LinearLayout@437430f8 | 
convertView 如我们所期待的非空了,在item11离开屏幕之后,它的视图(@437430f8)作为convertView容纳item21了
不同的项目布局(item layout)
我们再举一个稍微复杂的例子,在上例的list中加入一些分隔线
你需要做这些:
- 重(@Override)写 getViewTypeCount() – 返回你有多少个不同的布局
- 重写 getItemViewType(int) – 由position返回view type id
- 根据view item的类型,在getView中创建正确的convertView
以下是代码:
| 
1 
2 
3 
4 
5 
6 
7 
8 
9 
10 
11 
12 
13 
14 
15 
16 
17 
18 
19 
20 
21 
22 
23 
24 
25 
26 
27 
28 
29 
30 
31 
32 
33 
34 
35 
36 
37 
38 
39 
40 
41 
42 
43 
44 
45 
46 
47 
48 
49 
50 
51 
52 
53 
54 
55 
56 
57 
58 
59 
60 
61 
62 
63 
64 
65 
66 
67 
68 
69 
70 
71 
72 
73 
74 
75 
76 
77 
78 
79 
80 
81 
82 
83 
84 
85 
86 
87 
88 
89 
90 
91 
92 
93 
94 
95 
96 
97 
98 
99 
100 | publicclassMultipleItemsList
extendsListActivity
 {     privateMyCustomAdapter
 mAdapter;     @Override    publicvoidonCreate(Bundle
 savedInstanceState) {        super.onCreate(savedInstanceState);        mAdapter
 = newMyCustomAdapter();        for(inti
 = 1;
 i < 50;
 i++) {            mAdapter.addItem("item
 "+
 i);            if(i
 % 4==
0)
 {                mAdapter.addSeparatorItem("separator
 "+
 i);            }        }        setListAdapter(mAdapter);    }     privateclassMyCustomAdapter
extendsBaseAdapter
 {         privatestaticfinalintTYPE_ITEM
 = 0;        privatestaticfinalintTYPE_SEPARATOR
 = 1;        privatestaticfinalintTYPE_MAX_COUNT
 = TYPE_SEPARATOR + 1;         privateArrayList
 mData = newArrayList();        privateLayoutInflater
 mInflater;         privateTreeSet
 mSeparatorsSet = newTreeSet();         publicMyCustomAdapter()
 {            mInflater
 = (LayoutInflater)getSystemService(Context.LAYOUT_INFLATER_SERVICE);        }         publicvoidaddItem(finalString
 item) {            mData.add(item);            notifyDataSetChanged();        }         publicvoidaddSeparatorItem(finalString
 item) {            mData.add(item);            //
 save separator position            mSeparatorsSet.add(mData.size()
 - 1);            notifyDataSetChanged();        }         @Override        publicintgetItemViewType(intposition)
 {            returnmSeparatorsSet.contains(position)
 ? TYPE_SEPARATOR : TYPE_ITEM;        }         @Override        publicintgetViewTypeCount()
 {            returnTYPE_MAX_COUNT;        }         @Override        publicintgetCount()
 {            returnmData.size();        }         @Override        publicString
 getItem(intposition)
 {            returnmData.get(position);        }         @Override        publiclonggetItemId(intposition)
 {            returnposition;        }         @Override        publicView
 getView(intposition,
 View convertView, ViewGroup parent) {            ViewHolder
 holder = null;            inttype
 = getItemViewType(position);            System.out.println("getView
 "+
 position + "
 "+
 convertView + "
 type = "+
 type);            if(convertView
 == null)
 {                holder
 = newViewHolder();                switch(type)
 {                    caseTYPE_ITEM:                        convertView
 = mInflater.inflate(R.layout.item1, null);                        holder.textView
 = (TextView)convertView.findViewById(R.id.text);                        break;                    caseTYPE_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));            returnconvertView;        }     }     publicstaticclassViewHolder
 {        publicTextView
 textView;    }} | 
运行程序,你会看到每4个item一个分割线

看看日志,无异常,所有的convertView都是空的
| 02-0515:19:03.080:
 INFO/System.out(1035):
 getView 0nulltype
 = 002-0515:19:03.112:
 INFO/System.out(1035):
 getView 1nulltype
 = 002-0515:19:03.130:
 INFO/System.out(1035):
 getView 2nulltype
 = 002-0515:19:03.141:
 INFO/System.out(1035):
 getView 3nulltype
 = 002-0515:19:03.160:
 INFO/System.out(1035):
 getView 4nulltype
 = 102-0515:19:03.170:
 INFO/System.out(1035):
 getView 5nulltype
 = 002-0515:19:03.180:
 INFO/System.out(1035):
 getView 6nulltype
 = 002-0515:19:03.190:
 INFO/System.out(1035):
 getView 7nulltype
 = 002-0515:19:03.210:
 INFO/System.out(1035):
 getView 8nulltype
 = 002-0515:19:03.210:
 INFO/System.out(1035):
 getView 9nulltype
 = 1 | 
滚动list:
| 02-0515:19:54.160:
 INFO/System.out(1035):
 getView 10nulltype
 = 002-0515:19:57.440:
 INFO/System.out(1035):
 getView 11android.widget.LinearLayout@43744528type
 = 002-0515:20:01.310:
 INFO/System.out(1035):
 getView 12android.widget.LinearLayout@43744eb0type
 = 002-0515:20:01.880:
 INFO/System.out(1035):
 getView 13android.widget.LinearLayout@437456d8type
 = 002-0515:20:02.869:
 INFO/System.out(1035):
 getView 14nulltype
 = 102-0515:20:06.489:
 INFO/System.out(1035):
 getView 15android.widget.LinearLayout@43745f00type
 = 002-0515:20:07.749:
 INFO/System.out(1035):
 getView 16android.widget.LinearLayout@43747170type
 = 002-0515:20:10.250:
 INFO/System.out(1035):
 getView 17android.widget.LinearLayout@43747998type
 = 002-0515:20:11.661:
 INFO/System.out(1035):
 getView 18android.widget.LinearLayout@437481c0type
 = 002-0515:20:13.180:
 INFO/System.out(1035):
 getView 19android.widget.LinearLayout@437468a0type
 = 102-0515:20:16.900:
 INFO/System.out(1035):
 getView 20android.widget.LinearLayout@437489e8type
 = 002-0515:20:25.690:
 INFO/System.out(1035):
 getView 21android.widget.LinearLayout@4374a8d8type
 = 0 | 
convertView对于分割线是空的,直到第一个分割线可见,当其离开屏幕,视图去到Recycler并且convertView开始起作用。
 
                     
                    
                 
                    
                 
                
            
         
         浙公网安备 33010602011771号
浙公网安备 33010602011771号