最近遇到Gallery第一张图片死活加载不出来的bug。后来多次测试,才发现,当Gallery 的Adapter的getView 在position=0的时候会调用两次。
后来网上查了一下,大多数说法:第一次Gallery调用Adapter的getView的时候不知道加载多少个View才能填充满Gallery的宽度,所以,第一次调用是测试性的获取一个View,来measure 宽度,而不会用来显示,也不会用来缓存,暂时称为伪调用。而第二次调用,才是获得要真正显示的View。这也是本人项目中为什么第一章图片加载不来的原因----第一次请求图片,还没返回,而第二次调用请求图片时候,却因为防重复提交而请求失败。虽然第一次请求获取的View正确走了请求完图片并正确走了设置图片的回调,但是界面显示的却是第二次加载失败的View
网上常见的两种解决方案,看了一下,都很扯淡
1、根据getView 回调的参数 position 与parent.getChildCount 是否相等来判断,若相等,则为真调用,否则是伪调用。据测试,就算所有item正常加载完了,而且当前position非0,这时候调用notifydatachanged方法,依然会在这个位置上的getView调用两次,这样的操作,前边的判断很难成立,而且因为数据二次刷新或者分页刷新,这样的操作很常见。另外一种情况是,如果Item的参数是fillparent的话, getChildCount可能只返回0,1这两个值。
2、更扯淡的方法:根据postion 和view键值对本地缓存,对应位置有则返回,没有则重新初始化一个View。AdapterView的使用原本就是为了View的重复利用,如果这样本地缓存了,还有什么重复利用的意义?而且好多情况下,gallery如果是可以循环滚动的话,会在初始化的时候将adapter的getCount返回一个很大的值,如Integer,MAX_VALUE .且调用setSeleted()直接设置到一个很大的数,难道一直滚下去,就一直初始化新View下去?
没办法,只能用笨办法做一下判断和View的缓存了。
//与以下参数相关的代码都是为了防止postion=0的时候多次调用getView方法
private boolean first = true;
private View temp;
private int firstPosition = 0;
@Override
public View getView(int position, View convertView, ViewGroup parent) {
if(first){
//如果是因为Notify重复调用,这个position不一定等于1
firstPosition = position;
}else{
//后边的判断是因为Gallery使用了轮播
if(temp!=null&&(firstPosition==position||(getResourceCount()!=0&&position%getResourceCount()==firstPosition))){
return temp;
}else{
//如果不符合上边的条件,说明不是第一个位置(或者notify时候的当前位置)
temp = null;
}
}
if (convertView == null) {
convertView = mInflater.inflate(R.layout.gl_item, null);
if(first){
temp = convertView;
first = false;
}
}
fillView(convertView,position);
return converView;
}
@Override
public void notifyDataSetChanged() {
//因为notify的时候也会导致当前位置的getView重复调用
first = true;
super.notifyDataSetChanged();
}
目前bug解决了,但是不知道有没有更好的解决方案。或者说,我这个做法是不是更扯淡?