GridView异步加载中一次加载完所有数据问题的解决以及其原因分析

时间:2020-12-08 16:48:30

装载:http://www.jcodecraeer.com/a/anzhuokaifa/androidkaifa/2014/0918/1693.html


今天在开发一个相册应用的时候遇到一个很奇怪的问题,用于显示照片的GridView在显示的时候,初次加载,getView就被调用了1000次,而我的所有图片也只有1000张,也就是说在还没有滚动的情况下GridView就已经把所有的数据显示完了(当然超出屏幕的是看不见的),但是GridView本身是只显示视野范围内的数据项的啊。如果这样GridView的子view复用还有什么意义,GridView一直都是按需加载的啊。

下面是getView中打印出来的position值

GridView异步加载中一次加载完所有数据问题的解决以及其原因分析

显示安卓系统中图片是肯定需要异步加载的,如果一次就异步的方式去加载1000张照片,所消耗的系统资源可想而知,实际情况是我的应用直接就黑屏了。而即便没有开启异步加载如果第一次getView就调用了1000次,那么说明一次就生成了1000个子View,这样虽然应用不会死,也会出现上图中的渲染警告:Skipped 77 frames!  The application may be doing too much work on its main thread.

出现这个问题让我很无奈,因为我根本就不知道我到底哪里错了,我都是按照正常方式来使用GridView的。

经过无数次debug,终于找出了问题的所在。

为了复线出这种情况,先讲讲我是如何使用的。之所以把这两部分代码提出来,是因为用替换法我发现问题就出在这里。

用于显示照片的GridView的adapter中getView 是这样实现的:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
@Override
public View getView(final int position, View convertView, ViewGroup parent) {
     if (DEBUG)
         Log.i(TAG, "position = " + position);
     ViewHolder holder = null ;
     if (convertView == null ) {
         convertView = mLayoutInflater.inflate(R.layout.item_image,
                 null );
         holder = new ViewHolder();
         holder.imageView = (ImageView) convertView .findViewById(R.id.imageView);
         convertView.setTag(holder);
     }
     holder = (ViewHolder) convertView.getTag();
     mLoader.DisplayImage(mImageList.get(position), holder.imageView);
     return convertView;
}

其中mLoader.DisplayImage(mImageList.get(position), holder.imageView);是开启一个加载图片的线程,也就是异步加载。

R.layout.item_image代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
<?xml version= "1.0" encoding= "utf-8" ?>
<LinearLayout xmlns:android= "http://schemas.android.com/apk/res/android"
     android:layout_width= "fill_parent"
     android:layout_height= "wrap_content"
>
     <ImageView
         android:id= "@+id/imageView"
         android:layout_width= "wrap_content"
         android:layout_height= "wrap_content"
         android:scaleType= "fitXY"
         android:src= "@null" />
</LinearLayout>

如上使用,则会出现刚刚提到的怪异情况。

但是我发现在ImageView中,android:layout_height设置一个高度就不会出现这样的问题。或者是给ImageView设置一个padding也不会出现这样的问题,这些高度值我试了不同的数值,发现值越小,getView调用的次数越多,当为1px的时候差不多就接近1000次了,其实这个很好理解,因为值越小 每个item的高度越小,可见范围内就能显示越多的item。但是这个数值接近1000则给我我很大的触动。我一下意识到这个跟ImageViewlayout_heightwrap_content有关。

因为ImageView的图片资源是异步加载,所以在getView返回return convertView的时候ImageView其实是没有任何内容的,而wrap_content也就意味着其实际高度为0,因此不管你的ImageView在异步数据完成之后有多大,GridView都认为自己的高度足以显示完所有的item(因为在返回convertView的时候高度为0)。

找到了问题之后我们对症下药,完美解决。

但是还是有必要总结一下出现这种情况的时机,因为一般情况很少见,我看网上也很少有人提到这个问题,唯一看到有人对此提问还是在*上,而且没有人回答正确 http://*.com/questions/11152992/forbid-gridview-to-load-all-views-at-once

1.item的xml中的控件不管有多少层,必须是可能有一个高度为0的情况出现,比如ImageView或者是LinearLayout高度为wrap_content,如果是有类似于TextView的控件,则绝不会出出现这种bug,因为TextView是有一个默认最小高度的。

2.通常情况下发生在异步加载的时候,因为即便ImageView或者是LinearLayout高度为wrap_content,如果不是异步加载,他们的内容都会立马赋值,所以就会产生一个实际高度。

3.最普遍的是内容只是单个ImageView的情况,因为wrap_contentImageView在没有设置图片资源之前,高度是为0的。而且真正需要异步加载的往往也只有ImageView

最后给点建议:

虽然上面说item中有TextView绝不会出现无尽的加载完所有数据的异常情况,但是我们还是希望TextView(或者其他)能够在返回convertView之前确保高度是和实际内容一致的,不然即便是没有加载很多,也是多余预期的。