舉個例子吧,以好友列表為例
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:
[email protected]
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());
看到木有?