package org.aisen.android.ui.fragment;
import android.os.Bundle;
import android.text.TextUtils;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.AbsListView;
import android.widget.ListView;
import org.aisen.android.R;
import org.aisen.android.common.context.GlobalContext;
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.network.biz.IResult;
import org.aisen.android.network.task.TaskException;
import org.aisen.android.support.paging.IPaging;
import org.aisen.android.ui.fragment.adapter.IPagingAdapter;
import org.aisen.android.ui.fragment.itemview.AFooterItemView;
import org.aisen.android.ui.fragment.itemview.AHeaderItemViewCreator;
import org.aisen.android.ui.fragment.itemview.BasicFooterView;
import org.aisen.android.ui.fragment.itemview.IITemView;
import org.aisen.android.ui.fragment.itemview.IItemViewCreator;
import org.aisen.android.ui.fragment.itemview.OnFooterViewListener;
import org.aisen.android.ui.widget.AsToolbar;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;
/**
* 基于APagingFragment,可以扩展支持BaseAdapter的UI控件,并处理上、下拉的交互逻辑<br/>
*
* 1、维护Adapter<br/>
* 2、维护分页线程<br/>
* 3、处理UI界面的上、下拉<br/>
* 4、新增FooterView,用于ListView等界面的自动加载更多<br/>
* 5、当读取缓存数据并配置positionKey时,可以自动滑动到上次最后阅读位置<br/>
*
* @param <T>
* @param <Ts>
* @param <V>
*/
public abstract class APagingFragment<T extends Serializable, Ts extends Serializable, Header extends Serializable, V extends ViewGroup>
extends ABaseFragment
implements AsToolbar.OnToolbarDoubleClick, OnFooterViewListener, AFooterItemView.OnFooterViewCallback {
private static final String TAG = "AFragment-Paging";
public static final String PAGING_TASK_ID = "org.aisen.android.PAGING_TASK";
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";
private IPaging mPaging;// 分页器
private IPagingAdapter<T> mAdapter;
private APagingTask pagingTask;// 分页线程
RefreshConfig refreshConfig;// 刷新方面的配置
IItemViewCreator<T> mFooterItemViewCreator;
AFooterItemView<T> mFooterItemView;// FooterView,滑动到底部时,自动加载更多数据
AHeaderItemViewCreator<Header> mHeaderItemViewCreator;
public enum RefreshMode {
/**
* 重设数据
*/
reset,
/**
* 上拉,加载更多
*/
update,
/**
* 下拉,刷新最新
*/
refresh
}
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
if (savedInstanceState == null) {
refreshConfig = new RefreshConfig();
}
else {
refreshConfig = (RefreshConfig) savedInstanceState.getSerializable(SAVED_CONFIG);
}
ArrayList<T> datas = savedInstanceState == null ? new ArrayList<T>()
: (ArrayList<T>) savedInstanceState.getSerializable(SAVED_DATAS);
mAdapter = newAdapter(datas);
if (savedInstanceState != null && savedInstanceState.getSerializable(SAVED_PAGING) != null) {
mPaging = (IPaging) savedInstanceState.getSerializable(SAVED_PAGING);
} else {
mPaging = newPaging();
}
}
@Override
public void onSaveInstanceState(Bundle outState) {
// 将分页信息保存起来
if (mPaging != null)
outState.putSerializable(SAVED_PAGING, mPaging);
if (refreshConfig != null)
outState.putSerializable(SAVED_CONFIG, refreshConfig);
onSaveDatas(outState);
super.onSaveInstanceState(outState);
}
/**
* 数据量比较大的时候,子类可以不保存,会阻塞
*
* @param outState
*/
protected void onSaveDatas(Bundle outState) {
// 将数据保存起来
if (getAdapter() != null && getAdapter().getDatas().size() != 0)
outState.putSerializable(SAVED_DATAS, getAdapter().getDatas());
}
@Override
void _layoutInit(LayoutInflater inflater, Bundle savedInstanceSate) {
super._layoutInit(inflater, savedInstanceSate);
setupRefreshConfig(refreshConfig);
setupRefreshView(savedInstanceSate);
setupRefreshViewWithConfig(refreshConfig);
bindAdapter(getAdapter());
}
public IPagingAdapter getAdapter() {
return mAdapter;
}
public ArrayList<T> getAdapterItems() {
return mAdapter.getDatas();
}
public void onPullDownToRefresh() {
requestData(RefreshMode.refresh);
}
public void onPullUpToRefresh() {
requestData(RefreshMode.update);
}
@Override
public boolean isContentEmpty() {
return getAdapter() == null || getAdapter().getDatas().size() == 0;
}
// 子类不再允许重写这个类
@Override
final protected void onTaskStateChanged(ABaseTaskState state, TaskException exception) {
// super.onTaskStateChanged(state, tag);
}
/**
* 子类需要根据不同的刷新模式,来刷新自己的UI
*
* @param state
* @param exception
* @param mode
*/
protected void onTaskStateChanged(ABaseTaskState state, TaskException exception, RefreshMode mode) {
super.onTaskStateChanged(state, exception);
onTaskStateChanged(mFooterItemView, state, exception, mode);
if (state == ABaseTaskState.success) {
if (isContentEmpty()) {
if (emptyLayout != null && !TextUtils.isEmpty(refreshConfig.emptyHint))
ViewUtils.setTextViewValue(emptyLayout, R.id.txtLoadEmpty, refreshConfig.emptyHint);
}
}
else if (state == ABaseTaskState.falid) {
if (isContentEmpty()) {
if (loadFailureLayout != null && !TextUtils.isEmpty(exception.getMessage()))
ViewUtils.setTextViewValue(loadFailureLayout, R.id.txtLoadFailed, exception.getMessage());
}
}
else if (state == ABaseTaskState.finished) {
onRefreshViewFinished(mode);
}
}
public void setAdapterItems(ArrayList<T> items) {
mAdapter.getDatas().clear();
mAdapter.getDatas().addAll(items);
}
/**
* 子类配置
*
* @param config
*/
protected void setupRefreshConfig(RefreshConfig config) {
config.emptyHint = getString(R.string.comm_content_empty);
}
/**
* 设置分页
*
* @return <tt>null</tt> 不分页
*/
protected IPaging<T, Ts> newPaging() {
return null;
}
abstract public IItemViewCreator<T> configItemViewCreator();
/**
* 根据RefreshMode拉取数据
*
* @param mode
*/
abstract public void requestData(RefreshMode mode);
/**
* 列表控件
*
* @return
*/
abstract public V getRefreshView();
/**
* 配置Adapter
*
* @param datas
* @return
*/
abstract protected IPagingAdapter<T> newAdapter(ArrayList<T> datas);
/**
* 绑定Adapter到RefreshView
*
* @param adapter
*/
abstract protected void bindAdapter(IPagingAdapter adapter);
/**
* 设置列表控件状态为刷新状态
*
* @return true:某些控件,设置它的刷新状态,它会自己自动回调Callback去刷新数据,true即这种情况
*/
public boolean setRefreshViewToLoading() {
return false;
}
/**
* 根据Config刷新RefreshView
*/
protected void setupRefreshViewWithConfig(RefreshConfig config) {
}
/**
* 设置列表控件状态为刷新结束
*/
public void onRefreshViewFinished(RefreshMode mode) {
}
/**
* 初始化RefreshView
*
*/
protected void setupRefreshView(Bundle savedInstanceSate) {
if (refreshConfig != null && refreshConfig.footerMoreEnable) {
mFooterItemViewCreator = configFooterViewCreator();
View convertView = mFooterItemViewCreator.newContentView(getActivity().getLayoutInflater(), null, IPagingAdapter.TYPE_FOOTER);
mFooterItemView = (AFooterItemView<T>) mFooterItemViewCreator.newItemView(convertView, IPagingAdapter.TYPE_FOOTER);
}
mHeaderItemViewCreator = configHeaderViewCreator();
if (mFooterItemView != null) {
addFooterViewToRefreshView(mFooterItemView);
}
if (mHeaderItemViewCreator != null) {
addHeaderViewToRefreshView(mHeaderItemViewCreator);
}
}
/**
* 当前页面是否正在加载刷新
*
* @return
*/
public boolean isRefreshing() {
return pagingTask != null;
}
@Override
public boolean onToolbarDoubleClick() {
return false;
}
public static class RefreshConfig implements Serializable {
private static final long serialVersionUID = 6244426943442129360L;
public boolean pagingEnd = false;// 分页是否结束
public String positionKey = null;// 最后阅读坐标的Key,null-不保存,针对缓存数据有效
public boolean displayWhenScrolling = true;// 滚动的时候加载图片
public int releaseDelay = 5 * 1000;// 当配置了releaseItemIds参数时,离开页面后自动释放资源
public int[] releaseItemIds = null;// 离开页面时,释放图片的控件,针对ItemView
public String emptyHint = "数据为空";// 如果EmptyLayout中有R.id.txtLoadEmpty这个控件,将这个提示绑定显示
public boolean footerMoreEnable = true;// FooterView加载更多
}
/*********************************************开始数据刷新方法************************************************/
@Override
public void requestData() {
// 如果没有Loading视图,且数据为空,就显示FootView加载状态
RefreshMode mode = RefreshMode.reset;
if (getAdapter().getDatas().size() == 0 && loadingLayout == null)
mode = RefreshMode.update;
requestData(mode);
}
@Override
public void requestDataOutofdate() {
putLastReadPosition(0);
putLastReadTop(0);
requestDataSetRefreshing();
}
/**
* 设置刷新控件为刷新状态且刷新数据
*
*/
public void requestDataSetRefreshing() {
// 如果没有正在刷新,设置刷新控件,且子类没有自动刷新
if (!isRefreshing() && !setRefreshViewToLoading())
requestData(RefreshMode.reset);
}
public void requestDataDelaySetRefreshing(long delay) {
Runnable requestDelayRunnable = new Runnable() {
@Override
public void run() {
Logger.d(TAG, "延迟刷新,开始刷新, " + toString());
requestDataSetRefreshing();
}
};
runUIRunnable(requestDelayRunnable, delay);
}
/**
* 分页线程,根据{@link IPaging}构造的分页参数列表调用接口
*
* @author wangdan
*
* @param <Params>
* @param <Progress>
* @param <Result>
*/
public abstract class APagingTask<Params, Progress, Result extends Serializable> extends ABaseTask<Params, Progress, Result> {
final protected RefreshMode mode;
public APagingTask(RefreshMode mode) {
super(PAGING_TASK_ID);
this.mode = mode;
pagingTask = this;
if (mode == RefreshMode.reset && mPaging != null)
mPaging = newPaging();
}
@Override
protected void onPrepare() {
super.onPrepare();
Logger.d(TAG, toString() + "-" + ABaseTaskState.prepare + " - " + mode);
onTaskStateChanged(ABaseTaskState.prepare, null, mode);
}
@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;
}
bindAdapter(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)
IPagingAdapter.Utils.addItemsAtFrontAndRefresh(getAdapter(), resultList);
else if (mode == RefreshMode.update)
IPagingAdapter.Utils.addItemsAndRefresh(getAdapter(), resultList);
// 处理分页数据
if (mPaging != null) {
if (getAdapter() != null && getAdapter().getDatas().size() != 0)
mPaging.processData(result, (T) getAdapter().getDatas().get(0),
(T) getAdapter().getDatas().get(getAdapter().getDatas().size() - 1));
else
mPaging.processData(result, null, null);
}
// 如果是重置数据,重置canLoadMore
if (mode == RefreshMode.reset)
refreshConfig.pagingEnd = false;
// 如果数据少于这个值,默认加载完了
if (mode == RefreshMode.update || mode == RefreshMode.reset)
refreshConfig.pagingEnd = resultList.size() == 0;
// 如果是缓存数据,且已经过期
if (result instanceof IResult) {
// 这里增加一个自动刷新设置功能
IResult iResult = (IResult) result;
if (iResult.fromCache() && !iResult.outofdate())
toLastReadPosition();
if (mode == RefreshMode.reset || mode == RefreshMode.update) {
if (iResult.endPaging())
refreshConfig.pagingEnd = true;
else
refreshConfig.pagingEnd = false;
}
}
if (mode == RefreshMode.reset && getTaskCount(getTaskId()) > 1)
getAdapter().notifyDataSetChanged();
setupRefreshViewWithConfig(refreshConfig);
Logger.d(TAG, toString() + "-" + ABaseTaskState.success + " - " + mode);
onTaskStateChanged(ABaseTaskState.success, null, mode);
super.onSuccess(result);
}
@Override
protected void onFailure(TaskException exception) {
super.onFailure(exception);
Logger.d(TAG, toString() + "-" + ABaseTaskState.falid + " - " + mode + "-" + exception.getMessage());
onTaskStateChanged(ABaseTaskState.falid, exception, mode);
}
@Override
protected void onCancelled() {
super.onCancelled();
Logger.d(TAG, toString() + "-" + ABaseTaskState.canceled + " - " + mode);
onTaskStateChanged(ABaseTaskState.canceled, null, mode);
}
@Override
protected void onFinished() {
super.onFinished();
Logger.d(TAG, toString() + "-" + ABaseTaskState.finished + " - " + mode);
onTaskStateChanged(ABaseTaskState.finished, null, mode);
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;
}
/*********************************************结束数据刷新方法************************************************/
/*********************************************开始FooterView************************************************/
protected AHeaderItemViewCreator<Header> configHeaderViewCreator() {
return null;
}
protected IItemViewCreator<T> configFooterViewCreator() {
return new IItemViewCreator<T>() {
@Override
public View newContentView(LayoutInflater inflater, ViewGroup parent, int viewType) {
return inflater.inflate(BasicFooterView.LAYOUT_RES, parent, false);
}
@Override
public IITemView<T> newItemView(View convertView, int viewType) {
return new BasicFooterView<>(getActivity(), convertView, APagingFragment.this);
}
};
}
abstract protected void addFooterViewToRefreshView(AFooterItemView<?> footerItemView);
abstract protected void addHeaderViewToRefreshView(AHeaderItemViewCreator<?> headerItemViewCreator);
/**
* 设置FooterView为加载状态,
*
*/
@Override
public void setFooterViewToRefreshing() {
if (mFooterItemView != null) {
mFooterItemView.setFooterViewToRefreshing();
}
}
/**
* 当Task的状态发生改变时,刷新FooterView
*
* @param state
* @param exception
* @param mode
*/
@Override
public void onTaskStateChanged(AFooterItemView<?> footerItemView, ABaseTaskState state, TaskException exception, RefreshMode mode) {
if (refreshConfig == null || !refreshConfig.footerMoreEnable || mFooterItemView == null)
return;
if (mFooterItemView != null) {
mFooterItemView.onTaskStateChanged(footerItemView, state, exception, mode);
}
}
private boolean refreshViewScrolling = false;// 正在滚动
protected void onScrollStateChanged(int scrollState) {
// 滑动的时候,不加载图片
if (!refreshConfig.displayWhenScrolling) {
if (scrollState == AbsListView.OnScrollListener.SCROLL_STATE_FLING) {
refreshViewScrolling = true;
}
else if (scrollState == AbsListView.OnScrollListener.SCROLL_STATE_TOUCH_SCROLL) {
refreshViewScrolling = true;
}
else if (scrollState == AbsListView.OnScrollListener.SCROLL_STATE_IDLE) {
refreshViewScrolling = false;
}
}
if (scrollState == AbsListView.OnScrollListener.SCROLL_STATE_IDLE && // 停止滚动
!refreshConfig.pagingEnd && // 分页未加载完
refreshConfig.footerMoreEnable && // 自动加载更多
mFooterItemView != null // 配置了FooterView
) {
int childCount = getRefreshView().getChildCount();
if (childCount > 0 && getRefreshView().getChildAt(childCount - 1) == mFooterItemView.getConvertView()) {
setFooterViewToRefreshing();
}
}
// 保存最后浏览位置
if (scrollState == AbsListView.OnScrollListener.SCROLL_STATE_IDLE) {
if (!TextUtils.isEmpty(refreshConfig.positionKey) && getRefreshView() != null) {
putLastReadPosition(getFirstVisiblePosition());
putLastReadTop(getRefreshView().getChildAt(0).getTop());
}
}
}
@Override
public boolean canLoadMore() {
return refreshConfig == null || !refreshConfig.pagingEnd;
}
@Override
public void onLoadMore() {
if (canLoadMore()) {
onPullUpToRefresh();
}
}
protected void onScroll(int firstVisibleItem, int visibleItemCount, int totalItemCount) {
}
@Override
public boolean canDisplay() {
if (refreshConfig.displayWhenScrolling)
return true;
return !refreshViewScrolling;
}
/*********************************************结束FooterView************************************************/
/*********************************************开始阅读位置历史************************************************/
protected void toLastReadPosition() {
if (getRefreshView() == null || TextUtils.isEmpty(refreshConfig.positionKey))
return;
if (getRefreshView() instanceof ListView) {
runUIRunnable(new Runnable() {
@Override
public void run() {
ListView listView = (ListView) getRefreshView();
listView.setSelectionFromTop(getLastReadPosition(), getLastReadTop() + listView.getPaddingTop());
}
});
}
}
protected int getLastReadPosition() {
return ActivityHelper.getIntShareData(GlobalContext.getInstance(), refreshConfig.positionKey + "Position", 0);
}
protected void putLastReadPosition(int position) {
if (!TextUtils.isEmpty(refreshConfig.positionKey))
ActivityHelper.putIntShareData(GlobalContext.getInstance(), refreshConfig.positionKey + "Position", position);
}
protected int getLastReadTop() {
return ActivityHelper.getIntShareData(GlobalContext.getInstance(), refreshConfig.positionKey + "Top", 0);
}
protected void putLastReadTop(int top) {
if (!TextUtils.isEmpty(refreshConfig.positionKey))
ActivityHelper.putIntShareData(GlobalContext.getInstance(), refreshConfig.positionKey + "Top", top);
}
protected int getFirstVisiblePosition() {
return 0;
}
final protected IPaging getPaging() {
return mPaging;
}
/*********************************************结束阅读位置历史************************************************/
// 重构需要实现的方法
public void refreshUI() {
}
public void releaseImageViewByIds() {
}
}