[Android]ListView的Adapter.getView()方法中延遲加載圖片的優化


以下內容為原創,歡迎轉載,轉載請注明

來自天天博客:http://www.cnblogs.com/tiantianbyconan/p/4139998.html

 

舉個例子吧,以好友列表為例

ListView中每個Item表示一個好友,每個好友中都有一個頭像,需要從服務端加載到本地,然后顯示在item中。

顯然,啟動加載圖片的過程應該是在getView()方法中觸發,啟動一個線程,然后下載頭像圖片。這里使用我寫的一個開源框架ImageLoaderSamplehttps://github.com/wangjiegulu/ImageLoaderSample)來加載圖片,並實現內存緩存和本地緩存。

額--這里不再介紹ImageLoaderSample的用法了,給個傳送門:http://www.cnblogs.com/tiantianbyconan/p/3574131.html

 

再來看看getView()方法的調用時機:

1. Adapter調用NotifyDataChanged的時候

2. ListView滾動時,也就是convertView不斷復用的時候。

也就是說,每當ListView滾動時,getView()方法不斷被調用,圖片下載的過程不斷地執行(當然,ImageLoaderSample中會有緩存,但是內存緩存時有限的,如果內存緩存中找不到要顯示的圖片,那就需要到文件緩存中查找,需要進行io讀寫,這個也是相對比較耗時的),顯然,這里面還有優化的余地。

怎么去優化這里?只要讓ListView滾動的時候圖片顯示的時候不要去進行io讀寫就好了,具體邏輯如下:

-如果調用GetView方法時,ListView處於停止狀態,則先去內存中查找頭像圖片;如果內存圖片存在,則顯示內存中保存好的圖片;如果內存圖片不存在,則繼續到文件緩存中找,如果文件緩存圖片存在,則顯示文件緩存中的圖片;如果文件緩存圖片不存在,則根據url去網絡下載這張圖片,然后顯示;

-如果調用getView方法時,ListView處於滾動狀態,則去內存中查找頭像的圖片;如果內存圖片存在,則顯示內存中保存好的圖片;如果內存圖片不存在,則顯示一張默認的圖片(省去了從文件緩存中找圖片和網絡中去請求圖片的步驟)。

 

這樣的話,我們就必須要改寫BaseAdapter,讓它能夠監測ListView的滾動狀態,並在Adapter中可以獲取到當前ListView的滾動狀態。所以改造BaseAdapter,ABaseAdapterhttps://github.com/wangjiegulu/AndroidBucket/blob/master/src/com/wangjie/androidbucket/adapter/ABaseAdapter.java):

 1 package com.wangjie.androidbucket.adapter;
 2 
 3 import android.widget.*;
 4 import com.wangjie.androidbucket.adapter.listener.OnAdapterScrollListener;
 5 
 6 /**
 7  * Author: wangjie
 8  * Email: tiantian.china.2@gmail.com
 9  * Date: 12/3/14.
10  */
11 public abstract class ABaseAdapter extends BaseAdapter implements AbsListView.OnScrollListener {
12     private OnAdapterScrollListener onAdapterScrollListener;
13     /**
14      * 當前listview是否屬於滾動狀態
15      */
16     private boolean isScrolling;
17 
18     public boolean isScrolling() {
19         return isScrolling;
20     }
21 
22     public void setOnAdapterScrollListener(OnAdapterScrollListener onAdapterScrollListener) {
23         this.onAdapterScrollListener = onAdapterScrollListener;
24     }
25 
26     protected ABaseAdapter(AbsListView listView) {
27         listView.setOnScrollListener(this);
28     }
29 
30     @Override
31     public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) {
32         if (null != onAdapterScrollListener) {
33             onAdapterScrollListener.onScroll(view, firstVisibleItem, visibleItemCount, totalItemCount);
34         }
35     }
36 
37     @Override
38     public void onScrollStateChanged(AbsListView view, int scrollState) {
39         if (null != onAdapterScrollListener) {
40             onAdapterScrollListener.onScrollStateChanged(view, scrollState);
41         }
42 
43         // 設置是否滾動的狀態
44         if (scrollState == AbsListView.OnScrollListener.SCROLL_STATE_IDLE) { // 不滾動狀態
45             isScrolling = false;
46             this.notifyDataSetChanged();
47         } else {
48             isScrolling = true;
49         }
50     }
51 }

如上述代碼所示,該Adapter實現了AbsListView.OnScrollListener,並在構造方法中給ListView綁定了OnScrollListener,在實現的onScrollStateChanged方法中獲取到當前滾動狀態,並且保存這個狀態isScrolling,並暴露isScrolling()方法給外面。

OnAdapterScrollListener這個接口是繼承了AbsListView.OnScrollListener,因為這里在Adapter中一景設置了OnScrollListener了,所以如果在外面設置了新的OnScrollListener的話,就會失效了,所以必須提供另外一個setOnAdapterScrollListener,然后再傳入一個OnScrollListener,然后在每個方法中進行回調就好了,因為考慮到以后可能會擴展其他的接口方法,所以這里新寫了一個接口(為了以后擴展時原來的代碼不會被影響,推薦使用OnAdapterScrollSampleListener這個實現類來代替OnAdapterScrollListener這個接口,OnAdapterScrollSampleListener這個類只是對OnAdapterScrollListener的所有方法進行了空實現)。

 

然后我們編寫一個MyAdapter繼承ABaseAdapter,然后,在getView()方法中,需要顯示頭像的時候調用如下方法:

// 如果在滾動(從內存中查找,找不到也不進行網絡請求)

ImageLoader.getInstances().displayImage(headUrl, headIv, null, R.drawable.default_head, isScrolling());

看到木有?

1. displayImage()方法發生了改變,多了最后一個參數isOnlyMemory這個參數,表示是否只是在內存緩存中找這張圖片,如果沒有就不再繼續找下去了(displayImage原來的方法我還留着,所以不會影響之前的代碼)。

2. 調用了isScrolling()方法,作為參數isOnlyMemory的值,表示,如果正在滾動的話,就只在緩存中找這張圖片。

這樣,運行原來的代碼試試吧,是不是效率快了一些?

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM