基于Android官方AsyncListUtil優化經典ListView分頁加載機制(二)
我寫的附錄文章1,介紹了如何使用Android官方的分頁加載架構AsyncListUtil優化改進常見的RecyclerView分頁加載實作。AsyncListUtil作為一種通用的分頁加載架構,不僅可以套用在RecyclerView,也可也适用在經典(傳統)ListView中,下面給出一個簡單例子,說明如何通過AsyncListUtil調整ListView的分頁加載機制。
一個簡單的MainActivity适用AsyncListUtil和ListView,展示分頁加載:
package zhangphil.app;
import android.app.ListActivity;
import android.content.Context;
import android.graphics.Color;
import android.os.Bundle;
import android.os.SystemClock;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.v7.util.AsyncListUtil;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.AbsListView;
import android.widget.ArrayAdapter;
import android.widget.ListView;
import android.widget.TextView;
public class MainActivity extends ListActivity {
private final String TAG = "調試";
private final int NULL = -1;
private AsyncListUtil<DataItem> mAsyncListUtil;
private MyAdapter mAdapter;
private int mFirstVisibleItem = NULL, mVisibleItemCount = NULL;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
MyDataCallback mDataCallback = new MyDataCallback();
MyViewCallback mViewCallback = new MyViewCallback();
mAsyncListUtil = new AsyncListUtil(DataItem.class, 20, mDataCallback, mViewCallback);
mAdapter = new MyAdapter(this, NULL);
setListAdapter(mAdapter);
getListView().setOnScrollListener(new AbsListView.OnScrollListener() {
@Override
public void onScrollStateChanged(AbsListView absListView, int scrollState) {
mAsyncListUtil.onRangeChanged();
}
@Override
public void onScroll(AbsListView absListView, int firstVisibleItem, int visibleItemCount, int totalItemCount) {
mFirstVisibleItem = firstVisibleItem;
mVisibleItemCount = visibleItemCount;
}
});
}
@Override
protected void onListItemClick(ListView l, View v, int position, long id) {
mAsyncListUtil.refresh();
}
private class MyDataCallback extends AsyncListUtil.DataCallback<DataItem> {
@Override
public int refreshData() {
//更新資料的個數。
//假設預先設定更新若幹條。
return Integer.MAX_VALUE;
}
/**
* 在這裡完成資料加載的耗時任務。
*
* @param data
* @param startPosition
* @param itemCount
*/
@Override
public void fillData(DataItem[] data, int startPosition, int itemCount) {
Log.d(TAG, "fillData:" + startPosition + "," + itemCount);
for (int i = 0; i < itemCount; i++) {
DataItem dataItem = new DataItem();
dataItem.pos = startPosition + i;
dataItem.content = String.valueOf(System.currentTimeMillis());
data[i] = dataItem;
//模拟耗時任務,故意休眠一定時延。
SystemClock.sleep(100);
}
}
}
private class MyViewCallback extends AsyncListUtil.ViewCallback {
/**
* @param outRange
*/
@Override
public void getItemRangeInto(int[] outRange) {
outRange[0] = mFirstVisibleItem;
outRange[1] = mFirstVisibleItem + mVisibleItemCount;
/**
* 如果目前ListView為空,主動為使用者加載資料.
* 假設預先加載若幹條資料
*
*/
if (outRange[0] == NULL && outRange[1] == NULL) {
Log.d(TAG, "目前ListView為空!");
outRange[0] = 0;
outRange[1] = 9;
}
Log.d(TAG, "getItemRangeInto,目前可見position: " + outRange[0] + " ~ " + outRange[1]);
}
@Override
public void onDataRefresh() {
mAdapter.notifyDataSetChanged();
Log.d(TAG, "onDataRefresh");
}
@Override
public void onItemLoaded(int position) {
mAdapter.notifyDataSetChanged();
Log.d(TAG, "onItemLoaded:" + position);
}
}
private class MyAdapter extends ArrayAdapter {
private ViewHolder holder;
public MyAdapter(@NonNull Context context, int resource) {
super(context, resource);
}
@NonNull
@Override
public View getView(int position, @Nullable View convertView, @NonNull ViewGroup parent) {
if (convertView == null) {
convertView = LayoutInflater.from(getApplicationContext()).inflate(android.R.layout.simple_list_item_2, null);
holder = new ViewHolder(convertView);
convertView.setTag(holder);
} else {
holder = (ViewHolder) convertView.getTag();
}
holder.setData(getItem(position));
return convertView;
}
@Override
public int getCount() {
return mAsyncListUtil.getItemCount();
}
@Nullable
@Override
public DataItem getItem(int position) {
return mAsyncListUtil.getItem(position);
}
}
private class ViewHolder {
public TextView text1;
public TextView text2;
public ViewHolder(View view) {
text1 = view.findViewById(android.R.id.text1);
text1.setTextColor(Color.RED);
text2 = view.findViewById(android.R.id.text2);
text2.setTextColor(Color.BLUE);
}
public void setData(DataItem dataItem) {
if (dataItem == null) {
text1.setText("pos加載中...");
text2.setText("content加載中...");
} else {
text1.setText(String.valueOf(dataItem.pos));
text2.setText(dataItem.content);
}
}
}
private class DataItem {
public int pos;
public String content;
}
}
(一)和RecyclerView一樣,我在ListView中同樣适用ListView的滾動事件觸發AsyncListUtil的onRangeChanged,進而觸發分頁加載機制開始運作。
(二)作為示範,本例在ListView的普通item點選事件觸發一次AsyncListUtil的refresh事件,這種情況模拟當使用者長期停留在一個頁面需要為使用者主動重新整理資料的開發場景。
(三)一點兒特别注意:和附錄文章1在RecyclerView中的AsyncListUtil.DataCallback不同,在RecyclerView中的DataCallback,加載資料fillData方法的第一個參數data數組,如果作為基本資料類型如String,int等等這類,直接data[i]是沒有問題的,data[i]已經被AsyncListUtil建立和初始化完成,但是如果自定義資料類型,比如本例自定義了DataItem,意圖裝載一些開發者自定義的複雜類型,此時直接取出的data[i]為null!是以,為了解決這個問題,要從兩方面入手:
(a)一方面,如果是自定義的資料結構,在fillData中,每一個data[i]為其重新建立new出來一個對象執行個體,然後指派給data[i],本例是data[i]=new DataItem()。
(b)另一方面,在最後一關對View進行指派時候,判斷自定義類型是否為null,不會空指針時候才取出自定義資料結構中的資料元素(本例是DataItem中的成員變量)使用。
附錄:
1,《基于Android官方AsyncListUtil優化改進RecyclerView分頁加載機制(一)》連結:http://blog.csdn.net/zhangphil/article/details/78603499
2,《基于Android官方Paging Library的RecyclerView分頁加載架構》連結:http://blog.csdn.net/zhangphil/article/details/78627332