package com.banking.xc.utils; import java.util.ArrayList; import org.json.JSONException; import org.json.JSONObject; import android.os.Handler; import android.view.Gravity; import android.view.MotionEvent; import android.view.View; import android.view.View.OnTouchListener; import android.widget.AbsListView; import android.widget.AdapterView; import android.widget.AdapterView.OnItemSelectedListener; import android.widget.Gallery; import android.widget.GridView; import android.widget.ListView; import android.widget.TextView; import android.widget.AbsListView.OnScrollListener; import skytv_com.banking.enjoymovie.R; import com.banking.xc.utils.HttpGroup.HttpError; import com.banking.xc.utils.HttpGroup.HttpResponse; import com.banking.xc.utils.HttpGroup.HttpSetting; import com.banking.xc.utils.MyActivity.DestroyListener; /** * 不能一个NextPageLoader实例用于多个ListView */ public abstract class NextPageLoader implements HttpGroup.OnAllListener, DestroyListener { private final String TAG = "NextPageLoader"; private MyActivity myActivity; private Handler handler; private AdapterView adapterView; private MySimpleAdapter adapter; private View loadingView; protected ArrayList<Object> showItemList = new ArrayList<Object>();// 直接引用当前显示的数据 private boolean loading = false;// 标志位,保证滚动到屏幕底部的时候,不会多次触发加载 private ArrayList<?> nextItemList = null;// 用于预存下一页的数据 private boolean loadedShow = false;// 标志位,如果第二页还在下载的过程中就滚动到页尾部,可以通过该标志位申请下载后马上显示。 private boolean loadedLastPage = false; private boolean firstLoad = true;// 这个实例对象是否从来没从网络加载过数据,这是头次加载 private boolean isEffect = true;// 是否需要遮罩 private boolean isHolding = false;// 是否静止状态 private boolean isFling = false;// 是否滑行状态 private boolean hasNotify;// 是否存在数据改变通知 private boolean isPreloading;// 是否预加载 private boolean isPaging = true;// 是否分页加载 private int position; private OnScrollLastListener onScrollLastListener; protected HttpGroup httpGroup; protected JSONObject params; protected String pageNumParamKey = "page"; protected String pageSizeParamKey = "pagesize"; protected Integer pageNum = 1; protected Integer pageSize = 10; protected String noDataHint; protected String functionId; protected boolean httpNotifyUser = true;// 网络出错是否通知用户 private boolean isFinishing; /** * @author lijingzuo * * Time: 2010-12-26 上午10:31:40 * * Name: * * Description: * * @param myActivity * 因为这里建立的连接组或连接都要关联到某个myActivity去管理,所以要把myActivity传进来。 * @param listView * @param loadingView * 显示加载中的图标 * @param functionId * 跟服务器连接的functionId * */ public NextPageLoader(MyActivity myActivity, AdapterView adapterView, View loadingView, String functionId) { this.myActivity = myActivity; this.handler = myActivity.getHandler(); myActivity.addDestroyListener(this); httpGroup = this.myActivity.getHttpGroupaAsynPool(); this.adapterView = adapterView; this.loadingView = loadingView; this.functionId = functionId; this.isPreloading = true; } public NextPageLoader(MyActivity myActivity, AdapterView adapterView, View loadingView, String functionId, JSONObject params) { this(myActivity, adapterView, loadingView, functionId); this.params = params; this.isPreloading = true; } public NextPageLoader(MyActivity myActivity, AdapterView adapterView, View loadingView, String functionId, JSONObject params, String noData) { this(myActivity, adapterView, loadingView, functionId, params); this.noDataHint = noData; this.isPreloading = true; } public void setHttpNotifyUser(boolean httpNotifyUser) { this.httpNotifyUser = httpNotifyUser; } public void showPageOne() { // if (showItemList.size() < 1) { applyLoadedShow(); tryShowNextPage(); // } } /** * * Time: 2010-12-26 下午02:26:37 * * Name: * * Description: 是否加载过最后一页 * * @return * */ private boolean isLoadedLastPage() { // TODO Auto-generated method stub return loadedLastPage; } public abstract void setSelection(int postion); /** * @author lijingzuo * * Time: 2010-12-25 下午04:27:26 * * Name: * * Description: 当需要下载下一页数据时,加锁。目前没有正在下载的,那么返回true,可以加锁,否则返回false。 * * @return * */ private synchronized boolean loadingLock() { if (!loading) {// 如果没有加载中就改为加载中 loading = true; return loading; } return false; } /** * @author lijingzuo * * Time: 2010-12-25 下午04:27:29 * * Name: * * Description: 下载完毕时,解锁 * * */ private synchronized void loadingUnLock() { loading = false; } /** * @author lijingzuo * * Time: 2010-12-25 下午04:06:08 * * Name: * * Description: 下载后判断是否需要马上显示 * * @return * */ private boolean loadedShow() { if (loadedShow) { loadedShow = false; return true; } else { return false; } } class GalleryListener implements OnItemSelectedListener{ @Override public void onItemSelected(AdapterView<?> parent, View view, int position, long id) { int count = showItemList.size()-1; if(count == position){ if (isFinishing) { return; } if (!isLoadedLastPage()) { tryShowNextPage(); } } } @Override public void onNothingSelected(AdapterView<?> parent) { } } /** * @author lijingzuo * * Time: 2010-12-25 下午04:33:55 * * Name: * * Description: 申请要求下载后马上显示 * * @return * */ private void applyLoadedShow() { loadedShow = true; } /** * @author lijingzuo * * Time: 2010-12-25 下午03:59:12 * * Name: * * Description: 根据网络返回的httpResponse内容转换为列表需要的beanList。如果转换过程发生异常,无法满足基本显示要求 ,请返回空,此类会自动调用错误呈现。 * * @param httpResponse * @return * */ protected abstract ArrayList<?> toList(HttpResponse httpResponse); /** * @author lijingzuo * * Time: 2010-12-25 下午04:00:55 * * Name: * * Description: 错误呈现逻辑 * * */ protected abstract void showError(); /** * @author lijingzuo * * Time: 2010-12-25 下午04:06:12 * * Name: * * Description: 创建自定义 adapter * * @param context * @param itemList * @return * */ protected abstract MySimpleAdapter createAdapter(MyActivity myActivity, AdapterView adapterView, ArrayList<?> itemList); /** * @author lijingzuo * * Time: 2010-12-25 上午10:03:06 * * Name: * * Description: 显示下一页的逻辑。注意:应该在此方法前准备好数据;此方法应该执行在UI线程之外;此方法属于控制层 * * @param itemList * */ private void showNextPage(ArrayList<?> itemList) { // 下页已经投入使用 nextItemList = null; showItemList.addAll(itemList); if (Log.D) { System.out.println("showItemList size = " + showItemList.size()); } if (showItemList.size() < 1 && adapterView instanceof ListView && adapterView.getAdapter() == null) { TextView textView = new TextView(myActivity); textView.setGravity(Gravity.CENTER); if (null != this.noDataHint) { textView.setText(noDataHint); } else { textView.setText(R.string.no_data); } textView.setTextSize(17); textView.setPadding(0, 20, 0, 20); // ((ListView) listView).addHeaderView(textView); ((ListView) adapterView).addHeaderView(textView, R.string.no_data, false); } if (itemList.size() < pageSize || !isPaging) { // 最后一页 loadedLastPage = true; if (adapterView instanceof ListView) ((ListView) adapterView).setOnScrollListener(null); } else { if (Log.D) { System.err.println("showNextPage() isPreloading " + isPreloading); } pageNum++; loading();// 如果要显示这页就马上继续加载下一页 } if (null == adapter) { // 通过实现方法返回自定义的 adapter adapter = createAdapter(myActivity, adapterView, showItemList); adapter.setNextPageLoader(this); // XXX 下拉到低的时候触发,一般默认,预留扩展 final OnScrollLastListener onScrollLastListener = new OnScrollLastListener() { @Override public void onScrollLast() { if (isFinishing) { return; } if (!isLoadedLastPage()) { tryShowNextPage(); } } @Override public void onScrollFling() {// 滚动滑动时 isFling = true; } @Override public void onScrollIdle() {// 滚动停止时 isHolding = false; isFling = false; if (isFinishing) { return; } if (hasNotify) {// 是否需要更新 hasNotify = false; if (adapter != null) { adapter.notifyDataSetChanged(); } } checkLast(); } }; adapterView.setOnTouchListener(new ListView.OnTouchListener() { @Override public boolean onTouch(View v, MotionEvent event) { switch (event.getAction()) { case MotionEvent.ACTION_DOWN: isHolding = true; break; case MotionEvent.ACTION_UP: if (!isFling) { onScrollLastListener.onScrollIdle(); } break; } return false; } }); if (Log.D) { System.out.println("setAdapter adpter size = " + adapter.getCount()); } adapterView.setAdapter(adapter); if (adapterView instanceof ListView){ ((ListView) adapterView).setOnScrollListener(onScrollLastListener); }else if(adapterView instanceof GridView){ ((GridView) adapterView).setOnScrollListener(onScrollLastListener); }else if(adapterView instanceof Gallery){ ((Gallery) adapterView).setOnItemSelectedListener(new GalleryListener()); adapterView.setOnTouchListener(new OnTouchListener() { @Override public boolean onTouch(View v, MotionEvent event) { return false; } }); } loadingUnLock(); } else { adapter.notifyDataSetChanged(); loadingUnLock(); } // 通过简单的延迟方式以达到要求,应该准确地在能正确获取getChildCount时,马上执行。 handler.postDelayed(new Runnable() { public void run() { if (adapterView != null) { if (showItemList.size() <= adapterView.getChildCount()) {// 如果读取内容还不足一屏显示则马上尝试显示下一屏幕 tryShowNextPage(); } } } }, 500); if (isLoadedLastPage()) {// 如果加载过最后一页了,那么取出加载效果进度条 if (adapterView instanceof ListView) { ((ListView) adapterView).removeFooterView(loadingView); } } } /** * @author lijingzuo * * Time: 2011-1-24 下午08:31:37 * * Name: * * Description: 在加载前处理参数 * * */ protected void handleParamsBeforeLoading() { try { // XXX pageNumParamKey 已预留接口可以修改 getParams().put(pageNumParamKey, "" + pageNum); // XXX pageSizeParamKey 和 pageSize 已预留接口可以修改 getParams().put(pageSizeParamKey, "" + pageSize); } catch (JSONException e) { if (Log.V) { Log.v("NextPageLoader", "JSONException -->> ", e); } } } /** * @author lijingzuo * * Time: 2010-12-26 上午08:41:41 * * Name: * * Description: 非UI线程到网络取得数据后执行 * * */ protected void loading() { handleParamsBeforeLoading(); // XXX 连接方式以后可能会需要修改。届时可将此类改名,抽取出父类,此处由子类实现。 HttpSetting httpSetting = new HttpGroup.HttpSetting(); //httpSetting.setJsonParams(getParams()); httpSetting.setListener(this); httpSetting.setNotifyUser(httpNotifyUser); if (firstLoad && isEffect) {// 第一页需要遮罩层 httpSetting.setEffect(HttpSetting.EFFECT_DEFAULT); } else {// 第一页之后 httpSetting.setEffect(HttpSetting.EFFECT_NO); } firstLoad = false; httpGroup.add(httpSetting); // httpGroup.add(functionId, params, listener); } public void setEffect(boolean isEffect) { this.isEffect = isEffect; } /** * @author lijingzuo * * Time: 2010-12-25 下午04:39:38 * * Name: * * Description: 尝试显示下一页。每次滚动到最后一行会触发这个 * * */ private void tryShowNextPage() { if (loadedLastPage) {// 如果已经是最后一页,不作尝试 if (Log.D) { Log.v(TAG,"loadedLast Page " + loadedLastPage); } return; } if (null == nextItemList) {// 预加载数据为空才尝试加载 if (Log.D) { Log.v(TAG,"nextItemList == null isPreloading " + isPreloading); } applyLoadedShow();// 申请要求下载后马上显示 if (!loadingLock()) {// 如果不能加锁代表已经有在下载中 return; } if(Log.V){ Log.v(TAG,"isPreloading = "+isPreloading); } if (isPreloading) loading(); } else {// 预加载数据不为空,就直接显示 if (Log.D) { Log.d("Temp", "show do -->> "); System.err.println("showNextPage(nextItemList)"); } showNextPage(nextItemList); } } /** * @author lijingzuo * * Time: 2011-1-23 上午11:59:10 * * Name: * * Description: 被通知数据发生改变 * * */ public void notifyDataSetChanged() { if (!isHolding) { if (null != adapter) { adapter.notifyDataSetChanged(); } } else { hasNotify = true; } } /** * 是否分页 * * @return */ public boolean isPaging() { return isPaging; } /** * 设置是否启用分页 * * @param isPaging */ public void setPaging(boolean isPaging) { this.isPaging = isPaging; } /** * Copyright 2010 Jingdong Android Mobile Application * * @author lijingzuo * * Time: 2010-12-25 下午04:07:04 * * Name: * * Description: 滚动到最后一行的监听器逻辑 */ private abstract class OnScrollLastListener implements OnScrollListener { private int firstVisibleItem; private int visibleItemCount; private int totalItemCount; @Override public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) { this.firstVisibleItem = firstVisibleItem; this.visibleItemCount = visibleItemCount; this.totalItemCount = totalItemCount; setSelection(firstVisibleItem); } @Override public void onScrollStateChanged(AbsListView view, int scrollState) { switch (scrollState) { case OnScrollListener.SCROLL_STATE_TOUCH_SCROLL:// 滚动开始时 break; case OnScrollListener.SCROLL_STATE_FLING:// 滚动滑行时 onScrollFling(); break; case OnScrollListener.SCROLL_STATE_IDLE:// 滚动停止时 onScrollIdle(); break; } } public void checkLast() { if (firstVisibleItem + visibleItemCount == totalItemCount) { onScrollLast(); } } public abstract void onScrollFling();// 滚动滑行时 public abstract void onScrollIdle();// 滚动停止时 public abstract void onScrollLast();// 滚动到最后一项时 } /** * @author lijingzuo * * Time: 2010-12-26 上午09:59:14 * * Name: * * Description: 可以通过此方法自定义跟服务器通讯的pageNum参数名 * * @param pageNumParamKey * */ public void setPageNumParamKey(String pageNumParamKey) { this.pageNumParamKey = pageNumParamKey; } /** * @author lijingzuo * * Time: 2010-12-26 上午09:59:16 * * Name: * * Description: 可以通过此方法自定义跟服务器通讯的pageSize参数名 * * @param pageSizeParamKey * */ public void setPageSizeParamKey(String pageSizeParamKey) { this.pageSizeParamKey = pageSizeParamKey; } /** * @author lijingzuo * * Time: 2010-12-26 上午09:59:20 * * Name: * * Description: 可以通过此方法自定义每页显示的行数 * * @param pageSize * */ public void setPageSize(int pageSize) { this.pageSize = pageSize; } /** * @author lijingzuo * * Time: 2010-12-26 上午11:39:58 * * Name: * * Description: 获得的是引用,理论上可以在任何时候直接控制发出请求时的参数。 注意:页码和每页行数在发送前会被覆盖,请不要依靠此处控制,通过其他方法可以自定义,由内部控制; 最好在显示头一页之前就确定,开始使用后最好不要再去修改; 尽量不要被多线程控制和持有此对象,不实现多线程安全。 * * @return * */ public JSONObject getParams() { return null == params ? params = new JSONObject() : params; } /** * 修改数据(外部调用) */ public void modifyData(ModifyDataRunnable runnable) { if (!isHolding) { runnable.modifyData(this.showItemList); adapter.notifyDataSetChanged(); } else { // TODO 2011-05-10 任务队列 } } /** * 修改方法 */ public interface ModifyDataRunnable { void modifyData(ArrayList<Object> showItemList); } /* * 到网络取得数据后的逻辑 */ @Override public void onStart() { } @Override public void onEnd(HttpResponse httpResponse) { final ArrayList<?> itemList = toList(httpResponse); handler.post(new Runnable() { public void run() { if (isFinishing) { return; } if (null == itemList) {// 返回空就呈现错误提示 showError(); return; } nextItemList = itemList; if (loadedShow()) {// 是否马上显示 if (Log.D) { Log.d("Temp", "show now -->> "); System.err.println("showNextPage(itemList)"); } showNextPage(itemList);// 这个方法里面会自动又继续加载下一页 } } }); } public void setAdapterView(AdapterView adapterView) { if (this.adapterView == adapterView) { return; } this.adapterView.setVisibility(View.GONE); this.adapterView = null; this.adapter = null; this.adapterView = adapterView; if (this.adapterView != null) { this.adapterView.setVisibility(View.VISIBLE); isPreloading = false; showPageOne(); } if (Log.D) { System.out.println("adapterView is null " + this.adapterView); } } @Override public void onProgress(int max, int progress) { } @Override public void onError(HttpError error) { loadingUnLock(); showError(); } public ArrayList<?> getAllProductList() { return showItemList; } public Integer getPageNum() { return pageNum; } public Integer getPageSize() { return pageSize; } @Override public void onDestroy() { isFinishing = true; myActivity = null; adapterView = null; adapter = null; loadingView = null; showItemList = null; nextItemList = null; httpGroup = null; params = null; } }