package org.aisen.android.ui.fragment; 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.AbsListView.OnScrollListener; import android.widget.AbsListView.RecyclerListener; import android.widget.AdapterView; import android.widget.BaseAdapter; import android.widget.ImageView; import android.widget.ListView; import org.aisen.android.R; import org.aisen.android.common.utils.ActivityHelper; import org.aisen.android.common.utils.Logger; import org.aisen.android.common.utils.ViewUtils; import org.aisen.android.component.bitmaploader.BitmapLoader; import org.aisen.android.network.biz.IResult; import org.aisen.android.network.task.TaskException; import org.aisen.android.support.adapter.ABaseAdapter; import org.aisen.android.support.paging.IPaging; import org.aisen.android.ui.widget.AsToolbar; import com.nhaarman.listviewanimations.appearance.simple.SwingBottomInAnimationAdapter; import com.nineoldandroids.animation.Animator; import java.io.Serializable; import java.lang.ref.WeakReference; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Set; public abstract class ARefreshFragment<T extends Serializable, Ts extends Serializable, V extends View> extends ABaseFragment implements RecyclerListener, OnScrollListener, AsToolbar.OnToolbarDoubleClick, AdapterView.OnItemClickListener { private static final String TAG = "ARefresh"; private static final String SAVED_DATAS = "org.aisen.android.ui.Datas"; private static final String SAVED_PAGING = "org.aisen.android.ui.Paging"; private static final String SAVED_CONFIG = "org.aisen.android.ui.Config"; @SuppressWarnings("rawtypes") IPaging mPaging; private ABaseAdapter<T> mAdapter; private SwingBottomInAnimationAdapter swingAnimAdapter; @SuppressWarnings("rawtypes") private PagingTask pagingTask; private RefreshConfig refreshConfig; public enum RefreshMode { /** * 重设数据 */ reset, /** * 拉取更多 */ update, /** * 刷新最新 */ refresh } @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); ArrayList<T> datas = savedInstanceState == null ? new ArrayList<T>() : (ArrayList<T>) savedInstanceState.getSerializable(SAVED_DATAS); mAdapter = new MyBaseAdapter(datas, getActivity()); if (savedInstanceState != null && savedInstanceState.getSerializable(SAVED_PAGING) != null) { mPaging = (IPaging) savedInstanceState.getSerializable(SAVED_PAGING); } else { mPaging = configPaging(); } } @Override public void onSaveInstanceState(Bundle outState) { // 将分页信息保存起来 if (mPaging != null) outState.putSerializable(SAVED_PAGING, mPaging); onSaveDatas(outState); outState.putSerializable(SAVED_CONFIG, refreshConfig); super.onSaveInstanceState(outState); } /** * 数据量比较大的时候,子类可以不保存,会阻塞 * * @param outState */ protected void onSaveDatas(Bundle outState) { // 将数据保存起来 if (getAdapterItems() != null && getAdapterItems().size() != 0) outState.putSerializable(SAVED_DATAS, getAdapterItems()); } Handler mHandler = new Handler() { }; @Override void _layoutInit(LayoutInflater inflater, Bundle savedInstanceSate) { super._layoutInit(inflater, savedInstanceSate); if (getRefreshView() != null) { getRefreshView().setOnScrollListener(this); getRefreshView().setRecyclerListener(this); getRefreshView().setOnItemClickListener(this); } if (savedInstanceSate == null) { } else { refreshConfig = (RefreshConfig) savedInstanceSate.getSerializable(SAVED_CONFIG); } if (refreshConfig == null) { refreshConfig = new RefreshConfig(); configRefresh(refreshConfig); } if (refreshConfig.animEnable) { swingAnimAdapter = new SwingBottomInAnimationAdapter(mAdapter) { @Override protected Animator getAnimator(ViewGroup parent, View view) { return super.getAnimator(parent, view); } }; swingAnimAdapter.setAbsListView(getRefreshView()); } setInitRefreshView(getRefreshView(), savedInstanceSate); getRefreshView().setAdapter(getAdapter()); onChangedByConfig(refreshConfig); } @Override public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) { } private boolean isScrolling = false;// 正在滚动 @Override public void onScrollStateChanged(AbsListView view, int scrollState) { // 滑动的时候,不加载图片 if (!refreshConfig.ignoreScroll) { mHandler.removeCallbacks(refreshRunnable); if (scrollState == SCROLL_STATE_FLING) { isScrolling = true; } else if (scrollState == SCROLL_STATE_TOUCH_SCROLL) { isScrolling = true; } else if (scrollState == SCROLL_STATE_IDLE) { isScrolling = false; mHandler.postDelayed(refreshRunnable, 200); // notifyDataSetChanged(); } } // 保存最后浏览位置 if (scrollState == SCROLL_STATE_IDLE) { if (!TextUtils.isEmpty(refreshConfig.saveLastPositionKey) && getRefreshView() != null) { putLastReadPosition(getRefreshView().getFirstVisiblePosition()); putLastReadTop(getRefreshView().getChildAt(0).getTop()); } } } Runnable refreshRunnable = new Runnable() { @Override public void run() { Logger.w(TAG, "刷新视图"); notifyDataSetChanged(); } }; @Override public boolean canDisplay() { if (refreshConfig.ignoreScroll) return true; return !isScrolling; } @Override public void requestData() { requestData(RefreshMode.reset); } public BaseAdapter getAdapter() { if (swingAnimAdapter != null) return swingAnimAdapter; return mAdapter; } ABaseAdapter<T> getABaseAdapter() { return mAdapter; } public void onPullDownToRefresh() { requestData(RefreshMode.refresh); } public void onPullUpToRefresh() { if (!isRefreshing()) requestData(RefreshMode.update); } @Override public boolean isContentEmpty() { return getAdapterItems() == null || getAdapterItems().size() == 0; } /** * 分页线程,根据{@link IPaging}构造的分页参数列表调用接口 * * @author wangdan * * @param <Params> * @param <Progress> * @param <Result> */ public 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 && mPaging != null) mPaging = configPaging(); } @Override public Result workInBackground(Params... params) throws TaskException { String previousPage = null; String nextPage = null; if (mPaging != null) { previousPage = mPaging.getPreviousPage(); nextPage = mPaging.getNextPage(); } return workInBackground(mode, previousPage, nextPage, params); } @SuppressWarnings("unchecked") @Override protected void onSuccess(Result result) { if (result == null || getActivity() == null) { super.onSuccess(result); return; } if (getRefreshView().getAdapter() == null) getRefreshView().setAdapter(getAdapter()); 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 (mPaging != null) { if (getAdapterItems() != null && getAdapterItems().size() != 0) mPaging.processData(result, getAdapterItems().get(0), getAdapterItems().get(getAdapterItems().size() - 1)); else mPaging.processData(result, null, null); } // 如果是重置数据,重置canLoadMore if (mode == RefreshMode.reset) refreshConfig.canLoadMore = true; // 如果数据少于这个值,默认加载完了 if (mode == RefreshMode.update || mode == RefreshMode.reset) refreshConfig.canLoadMore = resultList.size() >= refreshConfig.minResultSize; // 如果是缓存数据,且已经过期 if (result instanceof IResult) { // 这里增加一个自动刷新设置功能 IResult iResult = (IResult) result; // 数据是缓存数据 if (iResult.isCache()) { // 缓存过期刷新数据 if (refreshConfig.expiredAutoRefresh && iResult.expired()) { requestDataDelay(500); } // 滑动到最后浏览位置 else { toLastReadPosition(); } } if (iResult.noMore()) refreshConfig.canLoadMore = false; } notifyDataSetChanged(); onChangedByConfig(refreshConfig); super.onSuccess(result); } @Override protected void onFinished() { super.onFinished(); if (isRefreshing()) onRefreshViewComplete(); 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); /** * 异步执行方法 * * @param mode 刷新模式 * @param previousPage 上一页页码 * @param nextPage 下一页页码 * @param params task参数 * @return * @throws TaskException */ 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); } /** * 设置刷新控件为刷新状态且刷新数据 * */ public void setRefreshingRequestData() { // 如果没有正在刷新,设置刷新控件,且子类没有自动刷新 if (!isRefreshing() && !setRefreshing()) requestData(RefreshMode.reset); } Runnable refreshDelay = new Runnable() { @Override public void run() { if (getRefreshView() instanceof ListView) { ListView listView = (ListView) getRefreshView(); listView.setSelectionFromTop(0, 0); } putLastReadPosition(0); putLastReadTop(0); setRefreshingRequestData(); } }; class MyBaseAdapter extends ABaseAdapter<T> { public MyBaseAdapter(ArrayList<T> datas, Activity context) { super(datas, context); } @Override protected AbstractItemView<T> newItemView() { return ARefreshFragment.this.newItemView(); } } private Map<String, WeakReference<View>> viewCache = new HashMap<>(); @Override public void onMovedToScrapHeap(View view) { if (!viewCache.containsKey(view.toString())) { Logger.d(TAG, "保存一个View到Cache"); viewCache.put(view.toString(), new WeakReference<View>(view)); } } /** * * @param container * @return true:子类自行释放,父类不做处理 */ protected boolean releaseImageView(View container) { if (configCanReleaseIds() != null) { for (int imgId : configCanReleaseIds()) { ImageView imgView = (ImageView) container.findViewById(imgId); if (imgView != null) { imgView.setImageDrawable(BitmapLoader.getLoadingDrawable(imgView)); Logger.v(ARefreshFragment.class.getSimpleName(), "释放ImageView"); } } } return false; } protected int[] configCanReleaseIds() { return null; } protected int delayRlease() { return 5 * 1000; } @Override public void onPause() { super.onPause(); mHandler.postDelayed(releaseRunnable, delayRlease()); } @Override public void onStop() { super.onStop(); // TODO 有手势返回的时候,不会调用onStop() // mHandler.removeCallbacks(releaseRunnable); // // releaseImageViewByIds(); } @Override public void onResume() { super.onResume(); mHandler.removeCallbacks(releaseRunnable); refreshUI(); } public void refreshUI() { mHandler.postDelayed(new Runnable() { @Override public void run() { notifyDataSetChanged(); } }, 200); _refreshUI(); } void _refreshUI() { } public void releaseImageViewByIds() { Logger.v(TAG, "releaseImageViewByIds()"); if (getRefreshView() != null) { int childSize = getRefreshView().getChildCount(); for (int i = 0; i < childSize; i++) { View view = getRefreshView().getChildAt(i); releaseImageView(view); if (viewCache.containsKey(view.toString())) { Logger.v(TAG, "已经释放了,从Cache中移除"); viewCache.remove(view.toString()); } } if (viewCache.size() > 0) { Set<String> keySet = viewCache.keySet(); for (String key : keySet) { View view = viewCache.get(key).get(); if (view != null) { Logger.v(TAG, "从Cache中释放一个View"); releaseImageView(view); } } } } } protected void toLastReadPosition() { if (getRefreshView() == null) return; getRefreshView().post(new Runnable() { @Override public void run() { if (getRefreshView() instanceof ListView) { ListView listView = (ListView) getRefreshView(); // listView.smoothScrollToPositionFromTop(getLastReadPosition(), getLastReadTop() + listView.getPaddingTop()); listView.setSelectionFromTop(getLastReadPosition(), getLastReadTop() + listView.getPaddingTop()); } } }); } protected int getLastReadPosition() { return ActivityHelper.getIntShareData(refreshConfig.saveLastPositionKey + "Position", 0); } protected void putLastReadPosition(int position) { if (!TextUtils.isEmpty(refreshConfig.saveLastPositionKey)) ActivityHelper.putIntShareData(refreshConfig.saveLastPositionKey + "Position", position); } private int getLastReadTop() { return ActivityHelper.getIntShareData(refreshConfig.saveLastPositionKey + "Top", 0); } protected void putLastReadTop(int top) { if (!TextUtils.isEmpty(refreshConfig.saveLastPositionKey)) ActivityHelper.putIntShareData(refreshConfig.saveLastPositionKey + "Top", top); } public void notifyDataSetChanged() { if (swingAnimAdapter != null) { // 刷新的时候,不显示动画 swingAnimAdapter.notifyDataSetChanged(); } else { mAdapter.notifyDataSetChanged(); } } public ArrayList<T> getAdapterItems() { return mAdapter.getDatas(); } private 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); } final protected RefreshConfig getRefreshConfig() { return refreshConfig; } @Override public void onItemClick(AdapterView<?> parent, View view, int position, long id) { } /** * 子类配置 * * @param config */ protected void configRefresh(RefreshConfig config) { } /** * 设置分页 * * @return <tt>null</tt> 不分页 */ protected IPaging<T, Ts> configPaging() { return null; } /** * Adapter的ItemView * * @return */ abstract protected ABaseAdapter.AbstractItemView<T> newItemView(); /** * 根据RefreshMode拉取数据 * * @param mode */ abstract protected void requestData(RefreshMode mode); /** * 列表控件 * * @return */ abstract public AbsListView getRefreshView(); /** * 设置列表控件状态为刷新状态 * * @return true:子类回调了刷新事件 */ abstract public boolean setRefreshing(); /** * 根据Config刷新RefreshView */ abstract protected void onChangedByConfig(RefreshConfig config); /** * 初始化RefreshView * * @param refreshView */ protected void setInitRefreshView(AbsListView refreshView, Bundle savedInstanceSate) { } /** * 设置列表控件状态为刷新结束 */ abstract public void onRefreshViewComplete(); public boolean isRefreshing() { return pagingTask != null; } Runnable releaseRunnable = new Runnable() { @Override public void run() { releaseImageViewByIds(); Logger.w("释放图片"); } }; @Override public boolean onToolbarDoubleClick() { return false; } @Override protected void taskStateChanged(ABaseTaskState state, Serializable tag) { super.taskStateChanged(state, tag); if (state == ABaseTaskState.success) { if (isContentEmpty()) { if (emptyLayout != null && !TextUtils.isEmpty(refreshConfig.emptyLabel)) ViewUtils.setTextViewValue(emptyLayout, R.id.txtLoadEmpty, refreshConfig.emptyLabel); } } else if (state == ABaseTaskState.falid) { if (isContentEmpty()) { if (loadFailureLayout != null && !TextUtils.isEmpty(tag + "")) ViewUtils.setTextViewValue(loadFailureLayout, R.id.txtLoadFailed, tag + ""); } } } public static class RefreshConfig implements Serializable { public static final long serialVersionUID = -963125420415611042L; public boolean expiredAutoRefresh = true;// 缓存数据过期自动刷新列表 public boolean canLoadMore = true;// 是否可以加载更多 public String saveLastPositionKey = null;// 最后阅读坐标的Key,null-不保存,针对缓存数据有效 public int minResultSize = 10;// 当加载的数据少于这个值时,默认没有更多加载 public boolean animEnable = false;// 是否启用加载动画 public String emptyLabel;// 加载空显示文本 public boolean ignoreScroll = true;// 置为false表示滑动列表时不加载图片 } }