在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中高效地应用此封装。(待续)