以下內容為原創,歡迎轉載,轉載請注明
來自天天博客:http://www.cnblogs.com/tiantianbyconan/p/4139998.html
舉個例子吧,以好友列表為例
ListView中每個Item表示一個好友,每個好友中都有一個頭像,需要從服務端加載到本地,然后顯示在item中。
顯然,啟動加載圖片的過程應該是在getView()方法中觸發,啟動一個線程,然后下載頭像圖片。這里使用我寫的一個開源框架ImageLoaderSample(https://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,ABaseAdapter(https://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的值,表示,如果正在滾動的話,就只在緩存中找這張圖片。
這樣,運行原來的代碼試試吧,是不是效率快了一些?