Android ListView 分页功能,包含前端分页,服务器分页
由于公司的需要,做了一个小组件。
先阶段只完成了2个功能:
- 前端分页功能。
- 服务器分页。
public class PaginationListview extends ListView {
public PaginationListview(Context context) {
super(context);
this.context = context;
init();
}
/**
* 初始化工作
*/
private void init() {
footView = createFootView();
addFooterView(footView);
initListener();
}
/**
* 创建listview的footview
*
* @return footView
*/
private View createFootView() {
LinearLayout ll = new LinearLayout(context);
AbsListView.LayoutParams params = new AbsListView.LayoutParams(
MATCH_PARENT, 50);
ll.setLayoutParams(params);
LinearLayout.LayoutParams params_m = new LinearLayout.LayoutParams(
MATCH_PARENT, MATCH_PARENT);
ll_loadingmore = new LinearLayout(context);
ll_loadingmore.setLayoutParams(params_m);
ll_loadingmore.setGravity(Gravity.CENTER);
ll_loadingmore.setOrientation(LinearLayout.HORIZONTAL);
addLoadView(ll);
addProgress();
addLoadMoreText();
ll.addView(ll_loadingmore);
return ll;
}
/**
* 加载更多的按钮
* @param parentViewGroup
*/
private void addLoadView(ViewGroup parentViewGroup) {
tv_more = new TextView(context);
LinearLayout.LayoutParams params_m = new LinearLayout.LayoutParams(
MATCH_PARENT, MATCH_PARENT);
tv_more.setLayoutParams(params_m);
tv_more.setGravity(Gravity.CENTER);
tv_more.setText(TEXT_LOADING);
tv_more.setTextColor(TEXT_COLOR_BLACK);
tv_more.setTextSize(TEXT_SIZE);
parentViewGroup.addView(tv_more);
}
/**
* 正在加载时的显示出来组件,添加一个进度圈
*/
private void addProgress() {
ProgressBar progressBar = new ProgressBar(context, null,
android.R.attr.progressBarStyleSmallInverse);
progressBar.setIndeterminate(true);
LinearLayout.LayoutParams params_w = new LinearLayout.LayoutParams(
WRAP_CONTENT, WRAP_CONTENT);
progressBar.setLayoutParams(params_w);
ll_loadingmore.addView(progressBar);
}
/**
* 正在加载时的显示出来组件,添加一个提示文字
*/
private void addLoadMoreText() {
TextView tv = new TextView(context);
LinearLayout.LayoutParams params_w = new LinearLayout.LayoutParams(
WRAP_CONTENT, WRAP_CONTENT);
tv.setLayoutParams(params_w);
tv.setText(TEXT_ON_LOADING);
tv.setTextColor(TEXT_COLOR_BLACK);
tv.setTextSize(TEXT_SIZE);
ll_loadingmore.addView(tv);
}
/**
*
* @param is
* 是否打开前台分页功能
*/
void setIsPageOpen(boolean is) {
this.isUIPage = is;
}
private class FootViewClick implements View.OnClickListener {
@Override
public void onClick(View arg0) {
doLoading();
}
}
private class OnScroll implements OnScrollListener {
@Override
public void onScroll(AbsListView view, int firstVisibleItem,
int visibleItemCount, int totalItemCount) {
if (isOver)
return;
lastItem = firstVisibleItem + visibleItemCount - 1;
}
@Override
public void onScrollStateChanged(AbsListView view, int scrollState) {
if (isOver)
return;
if (lastItem == getAdapter().getCount() - 1
&& scrollState == OnScrollListener.SCROLL_STATE_IDLE) {
doLoading();
}
}
}
private void initListener() {
if (loadWay == CLICK_TO_LOAD) {
footView.setOnClickListener(new FootViewClick());
} else if (loadWay == SCROLL_BOTTOM_LOAD) {
footView.setOnClickListener(null);
setOnScrollListener(new OnScroll());
tv_more.setText(TEXT_LOADING_SCROLL);
}
}
private void doLoading() {
tv_more.setVisibility(View.GONE);
ll_loadingmore.setVisibility(View.VISIBLE);
if (isUIPage) {
paginationAdapter.refreshCurtCount();
} else {
if (mLoadMore != null) {
mLoadMore.load();
}
}
}
/**
* 重写此方法,获取相关对象
*
* @param adapter
* 必须是PaginationAdapter类
*/
public void setAdapter(PaginationAdapter<?> adapter) {
setAdapter((ListAdapter) adapter);
this.paginationAdapter = adapter;
paginationAdapter.setListView(this);
}
/**
*
* @param loadMore
* 必须实现此接口中的load方法
*/
public void setLoadMore(LoadMore loadMore) {
this.mLoadMore = loadMore;
doLoading();
}
/**
* 异步获取数据的方法必须放在此处。典型的情况下:在这里发送一个request,在该请求的onSuccess中调用finishLoading。
*/
public interface LoadMore {
void load();
}
/**
* 当在某种情况下,例如在刷新页面,清空数据源后,调用此方法让listview的圈圈转起来。
*/
public void startLoading() {
doLoading();
}
/**
*
* 当加载完后,数据源增加完毕后必须调用此方法,典型的情况下,在请求成功或失败时调用此方法。
*
* @param isOver
* 如果确认已经加载完所有数据,传入true,此后将不再显示加载更多。若还有数据,参数为false
*/
public void finishLoading(boolean isOver) {
paginationAdapter.notifyDataSetChanged();
tv_more.setVisibility(View.VISIBLE);
ll_loadingmore.setVisibility(View.GONE);
this.isOver = isOver;
if (isOver) {
tv_more.setText(TEXT_FINISH_LOADING);
footView.setOnClickListener(null);
}
}
/**
* 设置加载的方式。
*
* @param way
* CLICK_TO_LOAD:用户点击"加载更多",才会加载。默认采用此种方式。
* SCROLL_BOTTOM_LOAD:当用户滑到底部的时候自动加载
*
*/
public void setLoadMoreWay(int way) {
loadWay = way;
initListener();
}
}
如果要实现比较简便的前端分页功能。比如我们公司就要控制一次显示出来的Item项数,以控制每项加载图片的线程,这时要继承一个Adapter:
public abstract class PaginationAdapter<T> extends BaseAdapter {
/**
* 更改无参构造方法,强制后代必须调用super(List);
*/
private PaginationAdapter() {
super();
Log.d(TAG, "private PaginationAdapter()");
}
/**
* 子类创建时必须调用此方法,以让本类获得数据源对象。
*
* @param list
* 数据源list集合。
*/
protected PaginationAdapter(List<T> list) {
super();
Log.d(TAG, "public PaginationAdapter(List<T> list)");
this.dataList = list;
if (dataList == null)
curtCount = 0;
else
curtCount = dataList.size();
}
@Override
public final int getCount() {
return curtCount;
}
@Override
public void notifyDataSetChanged() {
if (pageSize == 0) {
curtCount = dataList.size();
}
super.notifyDataSetChanged();
}
void refreshCurtCount() {
int toCount;
if (curtCount == 0 && firstPageSize != 0)
toCount = Math.min(curtCount + firstPageSize, dataList.size());
else
toCount = Math.min(curtCount + pageSize, dataList.size());
curtCount = toCount;
notifyDataSetChanged();
paginationListview.finishLoading(curtCount == dataList.size() ? true
: false);
}
void setListView(PaginationListview pListview) {
this.paginationListview = pListview;
if (pageSize > 0) {
paginationListview.setIsPageOpen(true);
refreshCurtCount();
}
}
/**
* 设置第一页的大小。若没有使用此方法,则默认使用setPageSize中的每页大小
*
* @param firstPageSize
*/
public void setFirsetPageSize(int firstPageSize) {
this.firstPageSize = firstPageSize;
}
/**
* 如果使用前端分页(Adapter在构建时,数据源就已经是完整的,只需要在此处设置每页/每次显示的大小即可)。如果后台分页,则不需要调用此方法
*
* @param pageSize
* 页面大小(每次点击加载更多时,增加显示的条目数量)
*/
public void setPageSize(int pageSize) {
this.curtCount = 0;
this.pageSize = pageSize;
}
/**
* 使用后台分页模式时,在服务器请求成功后,传入新增的list集合即可。
*
* @param list
* 新增加的list集合。注意此list必须和adapter构建时的list为同一类型。
* @param isOver
* 是否还有更多的数据,false表示不再继续显示加载更多
*/
public void addPageList(List<T> list, boolean isOver) {
for (T t : list)
dataList.add(t);
notifyDataSetChanged();
paginationListview.finishLoading(isOver);
}
}
使用方法非常简单。
前端分页方法:
// 使用前台分页方式。
List<String> strList = new ArrayList<String>();
for (int i = 0; i < 45; i++)
strList.add(String.valueOf(strList.size()) + " ~~");
lv3 = (PaginationListview) findViewById(R.id.paginationListview3);
adapter3 = new TestAdapter(this, strList);
// 设置首页大小和之后的每页大小
adapter3.setFirsetPageSize(20);
adapter3.setPageSize(10);
lv3.setAdapter(adapter3);
后台分页:
// 后台分页示例2
lv2 = (PaginationListview) findViewById(R.id.paginationListview2);
list = new ArrayList<String>();
adapter2 = new TestAdapter(this, list);
lv2.setAdapter(adapter2);
// 设置加载更多的方式
lv2.setLoadMoreWay(PaginationListview.SCROLL_BOTTOM_LOAD);
lv2.setLoadMore(new LoadMore() {
@Override
public void load() {
testRequest();
}
});
// 此处模拟向服务器发送异步请求
private void testRequest() {
Thread mThread = new Thread(new Runnable() {
@Override
public void run() {
try {
Thread.sleep(2000);
mHandler.sendEmptyMessage(TOAST_MSG_SEND);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
mThread.start();
}
// 模拟请求成功。
private Handler mHandler = new Handler() {
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
case TOAST_MSG_SEND:
// 修改数据源
for (int i = 0; i < 10; i++)
tempList.add("ListB" + tempList.size() + i);
adapter2.addPageList(tempList, false);//当数据加载完,传入true即可
}
}
};