package com.m.ui.fragment; import java.io.Serializable; import java.util.ArrayList; import java.util.List; import org.android.loader.BitmapLoader; import com.m.R; import com.m.common.settings.SettingUtility; import com.m.common.utils.ActivityHelper; import com.m.common.utils.Logger; import com.m.support.adapter.*; import com.m.support.adapter.ABaseAdapter.AbstractItemView; import com.m.support.iclass.IResult; import com.m.support.paging.IPaging; import com.m.support.paging.PagingProxy; import com.m.support.task.TaskException; import com.m.ui.activity.BaseActivity.IAcUnusedDoubleClickedHandler; import com.nhaarman.listviewanimations.appearance.simple.SwingBottomInAnimationAdapter; import android.animation.Animator; import android.app.Activity; import android.os.Bundle; import android.os.Handler; import android.text.TextUtils; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.widget.AbsListView; import android.widget.BaseAdapter; import android.widget.ListView; import android.widget.TextView; import android.widget.AbsListView.OnScrollListener; import android.widget.ImageView; import android.widget.AbsListView.RecyclerListener; public abstract class ARefreshFragment<T extends Serializable, Ts extends Serializable, V extends View> extends ABaseFragment implements RecyclerListener, OnScrollListener, IAcUnusedDoubleClickedHandler { public static final String TAG = "ARefresh"; private static final String SAVED_ADAPTER_DATAS = "com.m.ui.SAVED_ADAPTER_DATAS"; private static final String SAVED_PAGINGPROCESSORPROXY = "com.m.ui.SAVED_PAGINGPROCESSORPROXY"; @SuppressWarnings("rawtypes") PagingProxy mPagingProxy; private ABaseAdapter<T> mAdapter; private SwingBottomInAnimationAdapter swingAnimAdapter; @SuppressWarnings("rawtypes") private PagingTask pagingTask; private RefreshConfig refreshConfig; public enum RefreshMode { /** * 重设数据 */ reset, /** * 拉取更多 */ update, /** * 刷新最新 */ refresh } @SuppressWarnings({ "rawtypes", "unchecked" }) @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); ArrayList<T> datas = savedInstanceState == null ? new ArrayList<T>() : (ArrayList<T>) savedInstanceState.getSerializable(SAVED_ADAPTER_DATAS); mAdapter = new MyBaseAdapter(datas, getActivity()); if (savedInstanceState != null && savedInstanceState.getSerializable(SAVED_PAGINGPROCESSORPROXY) != null) { mPagingProxy = (PagingProxy) savedInstanceState.getSerializable(SAVED_PAGINGPROCESSORPROXY); } else { IPaging paging = configPaging(); if (paging != null) mPagingProxy = new PagingProxy<T, Ts>(paging); } refreshConfig = savedInstanceState == null ? new RefreshConfig() : (RefreshConfig) savedInstanceState.getSerializable("refreshConfig"); if (refreshConfig.animEnable) { swingAnimAdapter = new SwingBottomInAnimationAdapter(mAdapter) { @Override protected Animator getAnimator(ViewGroup parent, View view) { if (refreshConfig != null && !refreshConfig.animEnable) return null; return super.getAnimator(parent, view); } }; } } Handler mHandler = new Handler() { }; @Override void _layoutInit(LayoutInflater inflater, Bundle savedInstanceSate) { super._layoutInit(inflater, savedInstanceSate); if (getRefreshView() != null) { getRefreshView().setOnScrollListener(this); getRefreshView().setRecyclerListener(this); } if (refreshConfig == null) refreshConfig = new RefreshConfig(); if (swingAnimAdapter != null) swingAnimAdapter.setAbsListView(getRefreshView()); if (!refreshConfig.canLoadMore) resetRefreshView(refreshConfig); config(refreshConfig); if (emptyLayout != null) { TextView txtView = (TextView) emptyLayout.findViewById(R.id.txtLoadFailed); if (txtView != null) txtView.setText(refreshConfig.emptyLabel); } if (loadFailureLayout != null) { TextView txtView = (TextView) loadFailureLayout.findViewById(R.id.txtLoadFailed); if (txtView != null) txtView.setText(refreshConfig.faildLabel); } } /** * 子类配置 * * @param config */ protected void config(RefreshConfig config) { } final protected RefreshConfig getConfig() { return refreshConfig; } @Override public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) { } private boolean isScrolling = false; private boolean ignoreScroll = false;// 忽略滚动的情况,实时加载图片 @Override public void onScrollStateChanged(AbsListView view, int scrollState) { if (!ignoreScroll) { if (scrollState == SCROLL_STATE_FLING) { isScrolling = true; } else if (scrollState == SCROLL_STATE_TOUCH_SCROLL) { isScrolling = true; } else if (scrollState == SCROLL_STATE_IDLE) { isScrolling = false; notifyDataSetChanged(); if (!TextUtils.isEmpty(getLastReadKey()) && getRefreshView() != null) { putLastReadPosition(getRefreshView().getFirstVisiblePosition()); putLastReadTop(getRefreshView().getChildAt(0).getTop()); } } } } @Override public boolean canDisplay() { if (ignoreScroll) return true; return !isScrolling; } @Override public void onSaveInstanceState(Bundle outState) { // 将分页信息保存起来 if (mPagingProxy != null) outState.putSerializable(SAVED_PAGINGPROCESSORPROXY, mPagingProxy); saveRefreshListState(outState); outState.putSerializable("refreshConfig", refreshConfig); super.onSaveInstanceState(outState); } /** * 数据量比较大的时候,子类可以不保存,会阻塞 * * @param outState */ protected void saveRefreshListState(Bundle outState) { // 将数据保存起来 if (getAdapterItems() != null && getAdapterItems().size() != 0) outState.putSerializable(SAVED_ADAPTER_DATAS, getAdapterItems()); } @Override public void requestData() { requestData(RefreshMode.reset); } protected BaseAdapter getAdapter() { if (swingAnimAdapter != null) return swingAnimAdapter; return mAdapter; } ABaseAdapter<T> getABaseAdapter() { return mAdapter; } public void onPullDownToRefresh() { requestData(RefreshMode.refresh); } public void onPullUpToRefresh() { requestData(RefreshMode.update); } @Override public boolean isContentEmpty() { return getAdapterItems() == null || getAdapterItems().size() == 0; } /** * 分页线程,根据{@link IPaging}构造的分页参数列表调用接口 * * @author wangdan * * @param <Params> * @param <Progress> * @param <Result> */ protected abstract class PagingTask<Params, Progress, Result extends Serializable> extends ABaseTask<Params, Progress, Result> { final protected RefreshMode mode; public PagingTask(String taskId, RefreshMode mode) { super(taskId); this.mode = mode; pagingTask = this; if (mode == RefreshMode.reset && mPagingProxy != null) mPagingProxy.newInstance(); } @Override public Result workInBackground(Params... params) throws TaskException { String previousPage = null; String nextPage = null; if (mPagingProxy != null) { previousPage = mPagingProxy.getPreviousPage(); nextPage = mPagingProxy.getNextPage(); } return workInBackground(mode, previousPage, nextPage, params); } @SuppressWarnings("unchecked") @Override protected void onSuccess(Result result) { if (result == null || getActivity() == null) { super.onSuccess(result); return; } List<T> resultList; if (result instanceof List) resultList = (List<T>) result; else { resultList = parseResult(result); if (resultList == null) resultList = new ArrayList<T>(); } // 如果子类没有处理新获取的数据刷新UI,默认替换所有数据 if (!handleResult(mode, resultList)) if (mode == RefreshMode.reset) setAdapterItems(new ArrayList<T>()); // append数据 if (mode == RefreshMode.reset || mode == RefreshMode.refresh) addItemsAtFront(resultList); else if (mode == RefreshMode.update) addItems(resultList); notifyDataSetChanged(); // 处理分页数据 if (mPagingProxy != null) { if (getAdapterItems() != null && getAdapterItems().size() != 0) mPagingProxy.processData(result, getAdapterItems().get(0), getAdapterItems().get(getAdapterItems().size() - 1)); else mPagingProxy.processData(result, null, null); } // 如果是缓存数据,且已经过期 if (getTaskCount(getTaskId()) == 1 && result instanceof IResult) { // 这里增加一个自动刷新设置功能 if (SettingUtility.getPermanentSettingAsBool("pAutoRefresh", true)) { IResult iResult = (IResult) result; // 数据是缓存数据,显示缓存且同时刷新数据 if (iResult.isCache() && iResult.expired()) { requestDataDelay(700); } // 缓存数据没有过期,滚动到最后阅读位置 else if (iResult.isCache()) { toLastReadPosition(); } } else { toLastReadPosition(); } } // 如果是重置数据,重置canLoadMore if (mode == RefreshMode.reset) refreshConfig.canLoadMore = true; boolean oldCanLoadMore = refreshConfig.canLoadMore; // 没有更多数据了 if (result instanceof IResult && mode != RefreshMode.refresh) { IResult iResult = (IResult) result; if (iResult.noMore()) { refreshConfig.canLoadMore = false; } // 如果数据少于这个值,默认加载完了 else { refreshConfig.canLoadMore = resultList.size() >= refreshConfig.minResultSize; } } // 状态发生改变,重置刷新控件 if (oldCanLoadMore != refreshConfig.canLoadMore) { resetRefreshView(refreshConfig); } super.onSuccess(result); } @Override protected void onFinished() { super.onFinished(); if (isRefreshing()) setRefreshViewComplete(); pagingTask = null; } /** * 每次调用接口,获取新的数据时调用这个方法 * * @param mode * 当次拉取数据的类型 * @param datas * 当次拉取的数据 * @return <tt>false</tt> 如果mode={@link RefreshMode#reset} * 默认清空adapter中的数据 */ protected boolean handleResult(RefreshMode mode, List<T> datas) { return false; } /** * 将Ts转换成List(T) * * @param result * List(T) * @return */ abstract protected List<T> parseResult(Result result); /** * 异步执行方法,传入一个由 * {@link PagingProxy#generateHttpParams(RefreshMode)}构造的参数列表<br/> * 子类可在{@link #onPreParams(HttpParams)}中添加额外的参数 * * @param params * @return * @throws AisenException */ abstract protected Result workInBackground(RefreshMode mode, String previousPage, String nextPage, Params... params) throws TaskException; } public void requestDataDelay(int delay) { mHandler.removeCallbacks(refreshDelay); mHandler.postDelayed(refreshDelay, delay); } Runnable refreshDelay = new Runnable() { @Override public void run() { if (getRefreshView() instanceof ListView) ((ListView) getRefreshView()).setSelectionFromTop(0, 0); putLastReadPosition(0); putLastReadTop(0); // 如果没有正在刷新,设置刷新控件,且子类没有自动刷新 if (!isRefreshing() && !setRefreshing()) requestData(RefreshMode.reset); } }; class MyBaseAdapter extends ABaseAdapter<T> { public MyBaseAdapter(ArrayList<T> datas, Activity context) { super(datas, context); } @Override protected AbstractItemView<T> newItemView() { return ARefreshFragment.this.newItemView(); } } @Override public void onMovedToScrapHeap(View view) { Logger.v(ARefreshFragment.class.getSimpleName(), "onMovedToScrapHeap"); releaseView(view); } /** * * @param view * @return true:子类自行释放,父类不做处理 */ protected boolean releaseView(View view) { if (recyleImageViewRes() != null) { for (int imgId : recyleImageViewRes()) { ImageView imgView = (ImageView) view.findViewById(imgId); if (imgView != null) { imgView.setImageDrawable(BitmapLoader.getLoadingDrawable(imgView)); Logger.v(ARefreshFragment.class.getSimpleName(), "释放ImageView"); } } } return false; } protected int[] recyleImageViewRes() { return null; } @Override public void onStop() { super.onStop(); releaseBitmap(); } @Override public void onResume() { super.onResume(); ignoreScroll = ActivityHelper.getInstance().getBooleanShareData("com.m.IGNORE_SCROLL", false); refreshUI(); } public void refreshUI() { notifyDataSetChanged(); _refreshUI(); } void _refreshUI() { } public void releaseBitmap() { if (getRefreshView() != null) { int childSize = getRefreshView().getChildCount(); for (int i = 0; i < childSize; i++) { releaseView(getRefreshView().getChildAt(i)); } } } /** * 设置分页 * * @return <tt>null</tt> 不分页 */ protected IPaging<T, Ts> configPaging() { return null; } /** * 返回为null,不记录阅读状态 * * @return */ public String getLastReadKey() { return null; } protected void toLastReadPosition() { if (getRefreshView() == null) return; getRefreshView().post(new Runnable() { @Override public void run() { if (getRefreshView() instanceof ListView) ((ListView) getRefreshView()).setSelectionFromTop(getLastReadPosition(), getLastReadTop()); } }); } protected int getLastReadPosition() { return ActivityHelper.getInstance().getIntShareData(getLastReadKey() + "Position", 0); } protected void putLastReadPosition(int position) { if (refreshConfig.savePosition) ActivityHelper.getInstance().putIntShareData(getLastReadKey() + "Position", position); } private int getLastReadTop() { return ActivityHelper.getInstance().getIntShareData(getLastReadKey() + "Top", 0); } protected void putLastReadTop(int top) { if (refreshConfig.savePosition) ActivityHelper.getInstance().putIntShareData(getLastReadKey() + "Top", top); } @Override public boolean onAcUnusedDoubleClicked() { // 置顶 if (getRefreshView() != null && getRefreshView() instanceof ListView) ((ListView) getRefreshView()).setSelectionFromTop(0, 0); // 刷新 if (!isRefreshing() && SettingUtility.getPermanentSettingAsBool("com.m.ON_DOUBLE_CLICK_AC_TO_REFRESH", SettingUtility.getBooleanSetting("double_click_refresh"))) requestDataDelay(200); return true; } public void notifyDataSetChanged() { if (swingAnimAdapter != null) swingAnimAdapter.notifyDataSetChanged(); else mAdapter.notifyDataSetChanged(); } public ArrayList<T> getAdapterItems() { return mAdapter.getDatas(); } public int getAdapterCount() { if (swingAnimAdapter != null) return swingAnimAdapter.getCount(); return mAdapter.getCount(); } public void setAdapterSelected(int position) { mAdapter.setSelected(position); } public void setAdapterItems(ArrayList<T> items) { mAdapter.setDatas(items); } public void addItemsAtFront(List<T> items) { mAdapter.addItemsAtFront(items); } public void addItems(List<T> items) { mAdapter.addItems(items); } /** * Adapter的ItemView * * @return */ abstract protected AbstractItemView<T> newItemView(); /** * 根据RefreshMode拉取数据 * * @param mode */ abstract protected void requestData(RefreshMode mode); /** * 列表控件 * * @return */ abstract public AbsListView getRefreshView(); /** * 设置列表控件状态为刷新状态 * * @return true:子类回调了刷新事件 */ abstract public boolean setRefreshing(); /** * 设置列表FooterView显示Loading状态 * * @return */ public boolean setFooterRefreshing() { return false; } /** * 设置列表控件没有更多了 */ abstract public void resetRefreshView(RefreshConfig config); /** * 设置列表控件状态为刷新结束 */ abstract public void setRefreshViewComplete(); public boolean isRefreshing() { return pagingTask != null; } public static class RefreshConfig implements Serializable { private static final long serialVersionUID = -963125420415611042L; public boolean canLoadMore = true;// 是否可以加载更多 public boolean soundPlay = false;// 控件操作是否播放声音 public boolean savePosition = false;// 是否保存最后阅读位置 public String faildLabel;// 加载失败显示文本 public String emptyLabel;// 加载空显示文本 public int minResultSize = 10;// 当加载的数据少于这个值时,默认没有更多加载 public boolean animEnable = true;// 是否启用加载动画 } }