阅读本节,默认读者已经了解基本的ListView的优化技巧: ViewHolder, ConvertView
前边提到Ophone的ListView实现中有一个BUG。在我开发的过程中,使用了ListView的Header,放置两个按钮用来跳转到推荐的内容和“掌上应用汇”中的类似,它有4个按键用来导航到推荐的内容。
我使用的设备有5维的导航键,如果你一直用手操作,是不会出现任何问题的,因为手机一直处于TOUCH-MODE,如果用导航键,就会切换到NONE-TOUCH-MODE,不断按向下键切换到HEADER不可见,然后再不断按向上键切换到HEADER可见,这时上图中的4个按钮就会出现问题,表现为无法再收到onClick事件,而且按钮的按下的背景也无法显示(在正常的情况下一个按钮被按下时,会有黄色高亮)。在我的应用和“掌上应用汇”中都可以重现这个问题。可见Ophone中的ListView在处理HEADER View时存在bug,同样的应用程序在HTC Desire with MIUI上表现正常。
你可以使用本节介绍的内容来跳过这个bug,把要显示的View做为List中的第一个ITEM来模拟header效果。
而且我们的最终目标是
可以看到图中包含3中不同的ITEM
a) 灰色的title部分
b) 普通的条目
c) 当前title中更为详细”更多“
因为在ListView调用Adapter的
1
|
public
View getView(
int
position, View convertView, ViewGroup parent);
|
方法时,会把已经不可见的条目传回来,这样的好处是你不用创建一个新的View,只要使用传入的View,更新上面的内容就可以了,这样做可以大大的节约内存资源。
使用的方法就是判断convertView是否为null,如果不为null就使用,如果为null就创建新的。
在我未了解Adapter的机制前,为了实现返回3种不同的View,我写了一个FrameLayout,里面同时有这3种ITEM,只是根据不同的类型来改变不同View的setVisibility为GONE或者VISIBLE。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
|
public
View getView(
int
position, View convertView, ViewGroup parent){
if
(convertView ==
null
){
// 创建一个包含title, normal, more的整个的view
convertView = inflater.inflate(R.layout.layout, parent,
false
);
holder=
new
ViewHolder();
holder.title = convertView.findViewById(R.id.title);
// 省略的内容
convertView.setTag(holder);
}
else
{
holder= (ViewHolder)convertView.getTag();
}
Item item = getItem(position);
switch
(item.getType()){
case
TITLE:
holder.title.setVisibility(VISIBLE);
holder.normal.setVisibility(GONE);
holder.more.setVisibility(GONE);
holder.title.setText(item.title);
break
;
// 省略的内容
}
}
|
通过这种方法也能实现,而且使用convertView,效率也不算太差。缺点也很明显,明明创建了3套View,只是使用其中的1/3,如果有更多种,那就更浪费了。
其实ListView和Adapter早就已经为这种情况考虑到了,ListView在传回convertView时已经考虑到,对于不同的类型就缓存在不同的队列中,在调用时传回。请参考Adapter的如下方法:
1
2
|
public
int
getViewTypeCount();
public
int
getItemViewType(
int
position);
|
所以我们只要修改下adapter的实现就可以节约不少的资源。
修改后的:
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
|
public
int
getViewTypeCount(){
return
TOTAL_TYPE_COUNT;
//TOTAL_TYPE_COUNT = 3
}
public
int
getItemViewType(
int
position){
return
getItem(position).getType();
}
public
View getView(
int
position, View convertView, ViewGroup parent){
Item item = getItem(position);
switch
(item.getType()){
case
TITLE:
if
(convertView ==
null
){
// 仅创建一个包含title的View
convertView = inflater.inflate(R.layout.layout_title, parent,
false
);
holder=
new
ViewHolder();
holder.title = convertView.findViewById(R.id.title);
convertView.setTag(holder);
}
else
{
holder= (ViewHolder)convertView.getTag();
}
holder.title.setText(item.title);
break
;
// 省略的内容
}
}
|
如果模拟header的话就修改TOTAL_TYPE_COUNT = 4,再添加一种type就可以了,而且可以把这个header放到你想要的任何位置。这样就可以跳过那个bug.