由于公司的需要,做了一个小组件。
先阶段只完成了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即可 } } };