android的listview的一个关键技术就是重绘利用。
public View getView(int position, View convertView, ViewGroup parent) {
return null;
}
从Adatper的getview函数我们可以知道,函数提供了一个convertView的对象,这个对象是我们可以在一个列表中重复利用避免每次getview都进行重绘的关键。我们平常使用的都是大多是单个布局的item,所以我们可以通过建立一个holder就可以重复利用同一个结构的item。但是如果我们的布局中需要在列表中出先一些个别的item,他的结构跟别的不一样。比如第一项,或者最后一项跟其他项不一样,这时候该怎么做。很多人说直接通过position来判断就好了,在不同position的时候使用不同的布局。但是想法是很好的一实践就发现许多问题。
google官方文档在关于getview函数的时候有明确的提示,当遇到不同item结构的时候应该怎么处理只不过没有详细的实例。下面就根据自己遇到的问题举个实例。
google文档中:
convertView : The old view to reuse, if possible. Note: You should check that this view is non-null and of
an appropriate type before using. If it is not possible to convert this view to display the correct data, this method can create a new view. Heterogeneous lists can specify their number of view types, so that this View is always of the right type (see getViewTypeCount()
and getItemViewType(int)
).
也就是说如果你在对convertView操作的时候除了要判断他是不是为空,在多种布局的时候你还得通过getViewTypeCount()来指定你有几种布局,和通过getItemViewType()来获得你当前获得的是哪种结构的view。为什么要这么做,不能用position直接来判断呢,主要原因是你每次在通过getview获取到的convertView的时候你无法知道这个时候系统复用了哪个布局的view,这样你在使用converView的holder 的时候就有可能获取的不是你想要的holder这个时候,你就有可能报空指针异常之类的错误。好了既然知道了原理我们来看下具体的例子。
例子,原型是,第一个item使用了一个布局layout_one,其他所有的item使用的是布局layout_two。
代码:
在adapter里面定义几个常量
private static final int ITEM_LAYOUT_TYPE_COUNT = 2;
private static final int TYPE_ONE= 0;
private static final int TYPE_TWO = 1;
//重写 getViewTypeCount(), getItemViewType();
@Override
public int getViewTypeCount() {
return ITEM_LAYOUT_TYPE_COUNT;
}
// 当系统通过getview获取convertView的时候通过getItemViewType()可以获取到相应position的类型。
@Override
public int getItemViewType(int position) {
if(position == 0) {
return TYPE_ONE;
} else {
return TYPE_TWO
;
}
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
OneHolder oneHolder = new OneHolder();
TwoHolder twoHolder = new TwoHolder();
int layoutType = getItemViewType(position);
if(TYPE_ONE == layoutType) {
if(null == convertView) {
convertView = (LinearLayout) mLayoutInflater.inflate(R.layout.one_layout, null);
oneHolder.oneButton = (Button)convertView.findViewById(R.id.one_button);
convertView.setTag(oneHolder);
} else {
oneHolder = (OneHolder) convertView.getTag();
}
} else if(TYPE_TWO == layoutType) {
if(null == convertView) {
convertView = (LinearLayout) mLayoutInflater.inflate(R.layout.two_layout, null);
twoHolder.twoText = (TextView)convertView.findViewById(R.id.two_text);
convertView.setTag(twoHolder);
} else {
twoHolder = (TwoHolder) convertView.getTag();
}
}
return convertView;
}
当然你如果只是单纯不利用复用布局,使用position来做判断选择不同的布局也是可以的,但是如果你的这个布局不算太大的话是没太大问题的,但是如果你的布局稍微复杂点,也就是你每次进行滑动的时候系统会进行重绘,这个时候你就会发现界面一卡一卡的。