博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
android listview converView+ viewHolder
阅读量:4230 次
发布时间:2019-05-26

本文共 9372 字,大约阅读时间需要 31 分钟。

 

    以前写listview 的Adapter 的时候,貌似已经模式化了,竟然忘了为什么要用viewHolder只记得iviewHolder 是用来做缓存的。

  

ListView 和 Adapter 的基础

工作原理:

  1. ListView 针对List中每个item,要求 adapter “给我一个视图” (getView)。
  2. 一个新的视图被返回并显示

如果我们有上亿个项目要显示怎么办?为每个项目创建一个新视图?NO!这不可能!

实际上Android为你缓存了视图。

Android中有个叫做Recycler的构件,下图是他的工作原理:

  1. 如果你有10亿个项目(item),其中只有可见的项目存在内存中,其他的在Recycler中。
  2. ListView先请求一个type1视图(getView)然后请求其他可见的项目。convertView在getView中是空(null)的。
  3. 当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
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了

不同的项目布局(item layout)

我们再举一个稍微复杂的例子,在上例的list中加入一些分隔线

你需要做这些:

  1. 重(@Override)写 getViewTypeCount() – 返回你有多少个不同的布局
  2. 重写 getItemViewType(int) – 由position返回view type id
  3. 根据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
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开始起作用。

自己总结:viewHolder 相当于是android 用tag 存储的一个缓存视图,当有itemview从屏幕上消失不见时,系统会将原来的view 进行缓存,再根据tag把对应的view取出来。不同的viewHOlder 会存成不同vTag(根据GetItemViewType来判定哪个view)。假如你2种模式的布局 当发生回收的时候 你会用setTag分别记录是哪两种   这两种模式会被封装到viewholder中进行保存方便你下次使用。

convertview相当于是系统给的缓存视图,如果系统没有缓存,那么就inflate,不然就从缓存里去取。

    

转载地址:http://jvdqi.baihongyu.com/

你可能感兴趣的文章
sql2005数据库置疑修复断电崩溃索引损坏
查看>>
sql2000/sql2005/sql2008数据库变为0字节修复/MDF文件0字节恢复
查看>>
网吧计费系统数据库修复/网吧收银系统数据库恢复
查看>>
思迅商云8数据库修复/商超软件数据库恢复思迅附加数据库mdf823错误824错误断电
查看>>
数据恢复承诺(数据库修复承诺)
查看>>
系统升级/重装导致金蝶数据库账套丢失找回
查看>>
sql server数据库 18456错误修复
查看>>
sql2000数据库误删除后自行恢复二次覆盖成功恢复
查看>>
速达软件/管家婆软件/用友软件/金蝶软件/餐饮软件/财务软件数据库修复/数据初始化恢复
查看>>
数据库常见错误分析
查看>>
数据与数据库的区别/大数据需不需要备份和灾难恢复
查看>>
SQL Server数据库备份方法
查看>>
服务器数据库恢复/服务器重装系统/数据恢复注意事项
查看>>
数据库连接失败的原因及解决方法
查看>>
数据恢复过程千万不要做的事
查看>>
数据恢复问题分析及注意事项
查看>>
dell服务器服务器数据丢失后,数据恢复
查看>>
日常工作中,如何保证数据安全?
查看>>
mdf数据库文件数据修复/误删除格式化重装系统覆盖数据库数据恢复
查看>>
数据库错误5123修复
查看>>