GridView異步加載中一次加載完所有數據問題的解決以及其原因分析

RegShackelf 8年前發布 | 7K 次閱讀 Android開發 移動開發

今天在開發一個相冊應用的時候遇到一個很奇怪的問題,用于顯示照片的GridView在顯示的時候,初次加載,getView就被調用了1000次,而我的所有圖片也只有1000張,也就是說在還沒有滾動的情況下GridView就已經把所有的數據顯示完了(當然超出屏幕的是看不見的),但是GridView本身是只顯示視野范圍內的數據項的啊。如果這樣GridView的子view復用還有什么意義,GridView一直都是按需加載的啊。

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

顯示安卓系統中圖片是肯定需要異步加載的,如果一次就異步的方式去加載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 是這樣實現的:

@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代碼如下:

<?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)。

找到了問題之后我們對癥下藥,完美解決。

但是還是有必要總結一下出現這種情況的時機,因為一般情況很少見,我看網上也很少有人提到這個問題,唯一看到有人對此提問還是在stackoverflow上,而且沒有人回答正確 http://stackoverflow.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之前確保高度是和實際內容一致的,不然即便是沒有加載很多,也是多余預期的。

 本文由用戶 RegShackelf 自行上傳分享,僅供網友學習交流。所有權歸原作者,若您的權利被侵害,請聯系管理員。
 轉載本站原創文章,請注明出處,并保留原始鏈接、圖片水印。
 本站是一個以用戶分享為主的開源技術平臺,歡迎各類分享!