在Java語言開發中,大家對延遲加載並不陌生,比如Hibernate開發中就大量用到了延遲加載的技術。延遲加載在Android中同樣適用,其基本思想是:只有在必要的時候,纔去加載數據項。
本系列文章將對Android中常用的延遲加載進行簡要的總結,並給出示例代碼,以幫助讀者的理解。由於時間的限制,本系列文章只講解Android中的ListView和ImageView,其它涉及的組件較爲類似,因此不一一展開。
在android開發中ListView是比較常用的組件,它以列表的形式展示具體內容,並且能夠根據數據的長度自適應顯示。當需要從服務器獲取大量數據的時候,爲了節約用戶的網絡流量負載以及提高大數據量操作和顯示的速度,這個時候往往需要用到延遲加載的技術。
ListView的延遲加載技術中,endless比較有名,大家可以到https://github.com/commonsguy/cwac-endless下載相應的jar,作爲項目的類庫。由於網上有很多endless相關的文章,因此本文不再累述。本文的主要目的是封裝endless,尋找一種更加簡易、清晰和可擴展的方式,以求達到對ListView的延遲加載。下文逐步闡述:
首先定義個延遲加載數據類(LazyListData.java),此類非常簡單,抽象了List數據的總行數totalRows以及當前已有的行數據listData. List的數據類型通過<T>泛型(也可以用別的字母)的方式來定義。Serializable表示數據的可序列化。
import java.util.List;
public class LazyListData<T> implements java.io.Serializable{
private static final long serialVersionUID = 296133151360323907L;
private int totalRows;
private List<T> listData;
public LazyListData(int totalRows,List<T> listData){
this.totalRows = totalRows;
this.listData = listData;
}
public int getTotalRows() {
return totalRows;
}
public void setTotalRows(int totalRows) {
this.totalRows = totalRows;
}
public List<T> getListData() {
return listData;
}
public void setListData(List<T> listData) {
this.listData = listData;
}
}
其次定義個延遲加載的接口(LazyLoading.java),此類抽象了獲取下一頁數據的方法以及獲取數據後更新相應頁面的方法。startIndex是下一頁數據相對於整體數據的開始索引,endindex是下一頁相對於整體數據數據的結束索引
public interface LazyLoading{
public void cacheNextPageData(int startIndex,int endIndex);
public void updateItemView(View convertView, Object bean);
}
再次對EndlessAdapter進行封裝(LazyAdapter.java),參見以下代碼。
package com.whyonly.wrapper;
import java.util.List;
import android.content.Context;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ArrayAdapter;
import android.widget.ListView;
import com.commonsware.cwac.endless.EndlessAdapter;
import com.whyonly.R;
import com.whyonly.wrapper.LazyAdapter.LazyLoading;
public class LazyAdapter<T> extends EndlessAdapter{
private static final String TAG = "LazyAdapter";
private static final int PAGE_SIZE_LAZY = 20;
private Context mContext;
private int mItemResId;
private int mStartIndex_nextPage = 0;
private int mPageSize = 20;
private int mTotalRows = 0;
private LazyLoading mLazyLoading;
private ListView mListView;
public LazyAdapter(ListView listView,Context context,int itemResId,List<T> list,int pageSize,int totalRows,LazyLoading lazyLoading) {
super(new MyArrayAdapter1<T>(context,itemResId,list,lazyLoading));
Log.d(TAG,"LazyAdapter()");
mListView = listView;
mContext = context;
mItemResId = itemResId;
mStartIndex_nextPage = list.size();
mPageSize = pageSize;
mTotalRows = totalRows;
mLazyLoading = lazyLoading;
}
public LazyAdapter(ListView listView,Context context,int itemResId,List<T> list,int totalRows,LazyLoading lazyLoading) {
this(listView,context,itemResId,list,PAGE_SIZE_LAZY,totalRows,lazyLoading);
}
public LazyAdapter(Context context,int itemResId,List<T> list,int totalRows,LazyLoading lazyLoading) {
this(null,context,itemResId,list,PAGE_SIZE_LAZY,totalRows,lazyLoading);
}
public interface LazyLoading{
public void cacheNextPageData(int startIndex,int endIndex);
public void updateItemView(View convertView, Object bean);
}
@Override
protected View getPendingView(ViewGroup parent) {
Log.d(TAG,"getPendingView()");
LayoutInflater vi = (LayoutInflater)mContext.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
View row=vi.inflate(R.layout.progressrow, null);
if(mStartIndex_nextPage >= mTotalRows){//hidden the recycle
row.findViewById(R.id.progressBar1).setVisibility(View.GONE);
row.setVisibility(View.GONE);
}
return row;
}
@Override
protected boolean cacheInBackground() {
Log.d(TAG,"getWrappedAdapter().getCount()="+getWrappedAdapter().getCount()+",mTotalRows="+mTotalRows);
if(getWrappedAdapter().getCount()<mTotalRows){
int endIndex_nextPage = Math.min(mStartIndex_nextPage + mPageSize -1,mTotalRows-1);
mLazyLoading.cacheNextPageData(mStartIndex_nextPage,endIndex_nextPage);
mStartIndex_nextPage = endIndex_nextPage + 1;
return true;
}else{
Log.d(TAG,"cacheInBackground() return false");
return false;
}
}
@Override
protected void appendCachedData() {
}
}
class MyArrayAdapter1<T> extends ArrayAdapter<T> {
private Context mContext;
private LazyLoading mLazyLoading;
private int mItemResId;
public MyArrayAdapter1(Context context, int itemResId, List<T> items,LazyLoading lazyLoading) {
super(context, 0, items);
this.mContext = context;
mItemResId = itemResId;
this.mLazyLoading = lazyLoading;
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
View item = convertView;
if (item == null) {
LayoutInflater vi = (LayoutInflater)mContext.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
item = vi.inflate(mItemResId, null);
}
final T bean = getItem(position);
mLazyLoading.updateItemView(item, bean);
return item;
}
}
在此類中,R.layout.progressrow表示加載下一頁代碼的等待視圖,等待視圖的目的是提示用戶正在加載,使得頁面比較平滑和增加用戶友好性,佈局代碼如下:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout android:id="@+id/linearLayout1"
android:layout_width="fill_parent" android:layout_height="wrap_content"
xmlns:android="http://schemas.android.com/apk/res/android"
android:gravity="center"
>
<ProgressBar android:id="@+id/progressBar1" android:paddingTop="3dip"
android:layout_width="wrap_content" android:layout_height="wrap_content"></ProgressBar>
</LinearLayout>
至此,Listview延遲加載的封裝基本結束,接下來將通過一個示例講解如何在Activity中高效地應用此封裝。(待續)