android 下拉刷新数据,如何剥离Android页面下拉刷新、加载下一页等逻辑?

最近碰到一个新的页面控制需求:下拉刷新如果失败,listview上面的数据需要保留,然后悲剧的发现之前写的NetFragment和ListNetFragment都不能覆盖这种逻辑,又要重写了。痛定思痛,我发现问题的本质在于 控制逻辑C 和 页面展示V 没有真正分离,因此决定先把逻辑从Fragment里面抽取出来。

一、建立状态模型

所谓的逻辑,在这里是一个状态机,包含的状态如下:

请求数据状态:请求前、请求中、请求结束

请求后的状态有五种:

无法访问网络;

服务器连接失败;

请求参数异常;

数据为空;

获取到有效数据,如果是list意味着长度大于0;

建立状态模型如下:

public enum STATE {

ASK_PRE(-3), // 请求前

ASK_ING(-2), // 请求中

ASK_ED(-1), // 请求结束

ASK_ED_CANNOT_ACCESS(0), //无法访问网络;

ASK_ED_FAIL(1), //服务器连接失败;

ASK_ED_ERROR(2), //请求参数异常

ASK_ED_EMPTY(3), //数据为空;

ASK_ED_AVAILABILITY(4),; //获取到有效数据

final int value;

STATE(int i) {

value = i;

}

}

二、写控制逻辑

基本思路:根据网络请求结果,控制八种状态的流转,至于上层如何处理这些状态下的布局,底层完全不控制。最后从NetFragment中剥离出NetController。

/**

* Created by shitianci on 16/8/23.

*/

public abstract class NetController {

private static final String TAG = NetController.class.getSimpleName();

protected final Context mContext;

boolean loadingNetData = false;

public NetController(Context context) {

mContext = context;

}

public void loadNetData() {

Log.d(TAG, "loadNetData, loadingNetData = " + loadingNetData);

showData(STATE.ASK_PRE, null);

if (loadingNetData) {

showData(STATE.ASK_ED, null);

return;

}

if (!BaseRepositoryCollection.tryToDetectNetwork(mContext)) {

showData(STATE.ASK_ED, null);

showData(STATE.ASK_ED_CANNOT_ACCESS, null);

return;

}

SimpleSafeTask netTask = new SimpleSafeTask(mContext) {

protected void onPreExecuteSafely() throws Exception {

loadingNetData = true;

showData(STATE.ASK_ING, null);

}

@Override

protected T doInBackgroundSafely() throws Exception {

T result = onDoInBackgroundSafely();

return result;

}

@Override

protected void onPostExecuteSafely(T result, Exception e) {

showData(STATE.ASK_ED, null);

super.onPostExecuteSafely(result, e);

loadingNetData = false;

if (e != null || result == null) {

showData(STATE.ASK_ED_FAIL, result);

return;

}

if (result.getRespCode() != NetResultInfo.RETURN_CODE_000000) {

showData(STATE.ASK_ED_ERROR, result);

return;

}

if (isEmpty(result)){

showData(STATE.ASK_ED_EMPTY, result);

return;

}

showData(STATE.ASK_ED_AVAILABILITY, result);

}

protected void onCancelled() {

loadingNetData = false;

showData(STATE.ASK_ED, null);

showData(STATE.ASK_ED_FAIL, null);

}

};

netTask.execute();

return;

}

/**

* -------------------------

* START: 最重要的流程方法

* -------------------------

*/

/**

* 加载后台数据

*

* @return

*/

protected abstract T onDoInBackgroundSafely();

/**

* 数据是否为空

* 针对list情形,如果list size为0,则返回为true。

* @param result

* @return

*/

protected abstract boolean isEmpty(T result);

/**

* 返回状态

* @param state

* @param result

*/

protected abstract void showData(STATE state, T result);

/**

* -------------------------

* END

* -------------------------

*/

}

难点:ListView顶部的数据如何处理?

第一种思路是嵌套,形成如下结构:

SwipeRefreshLayout

* ScrollView

* LinearLayout

* topView

* ListView

经过实践,发现ListView无法获取到上拉的动作,因此无法加载下一页,这跟SwipeRefreshLayout的限制有关,SwipeRefreshLayout只能处理第一层的子view,所以此路不通。

第二种思路是把topView作为ListView的HeaderView,

SwipeRefreshLayout

* ListView

* topView

这样处理之后层级就变得极为简单。经过检验,滑动正常,剩下的问题是,如何在数据异常时,显示异常界面?——利用ArrayAdapter加载不同数据类型的特性,把异常界面当做不通类型的数据来进行适配。经过检验,显示正常。抽取出来的ListNetController代码如下:

/**

* Created by shitianci on 16/8/23.

*/

public abstract class ListNetController extends NetController> {

private String TAG = ListNetController.class.getSimpleName();

public enum Type {

REFRESH,

LOAD_MORE,

}

private final SwipeRefreshLayout mSwipeRefreshLayout;

private SwipeRefreshHelper mSwipeRefreshHelper;

private final ListView mListView;

private final O mMockData; //用于模拟不正常的数据

public DataAdapter mDataAdapter = null;

Type type = Type.REFRESH;

public ListNetController(Context context, SwipeRefreshLayout swipeRefreshLayout, ListView listView) {

this(context, swipeRefreshLayout, listView, null, null);

}

/**

*

* @param context 上下文环境

* @param swipeRefreshLayout 下拉刷新布局

* @param listView 列表布局

* @param headerView 头部显示布局

* @param abnormalData 异常数据,上层new一个即可。

*/

public ListNetController(Context context, SwipeRefreshLayout swipeRefreshLayout, ListView listView, View headerView, O abnormalData) {

super(context);

mSwipeRefreshLayout = swipeRefreshLayout;

mListView = listView;

mListView.addHeaderView(headerView);

mDataAdapter = new DataAdapter(mContext);

mListView.setAdapter(mDataAdapter);

mMockData = abnormalData;

configRefreshLayout();

}

/**

* 下拉刷新数据

*/

public void refresh() {

mSwipeRefreshHelper.autoRefresh();

}

protected void configRefreshLayout() {

//!!!为了保证能够加载下一页,这个方法必须调用,且必须放在mSwipeRefreshHelper初始化前面,具体原因看代码

mSwipeRefreshLayout.setColorSchemeColors(panda.android.lib.R.color.app_primary);

mSwipeRefreshHelper = new SwipeRefreshHelper(mSwipeRefreshLayout);

// try {

// Field field = mSwipeRefreshHelper.getClass().getDeclaredField("mContentView");

// field.setAccessible(true);

// field.set(mSwipeRefreshHelper, listView); //修改内容

// } catch (Exception e) {

// e.printStackTrace();

// }

mSwipeRefreshHelper.setLoadMoreEnable(true);

mSwipeRefreshHelper.setOnSwipeRefreshListener(new SwipeRefreshHelper.OnSwipeRefreshListener() {

@Override

public void onfresh() {

Log.d(TAG, "下拉刷新");

// mDataAdapter = null;

loadNetData();

isLoadedAllNetData = false;

type = Type.REFRESH;

}

});

mSwipeRefreshHelper.setOnLoadMoreListener(new OnLoadMoreListener() {

@Override

public void loadMore() {

Log.d(TAG, "加载更多, isLoadedAllNetData = " + isLoadedAllNetData);

if (isLoadedAllNetData) {

mSwipeRefreshHelper.loadMoreComplete(false);

return;

}

loadNetData();

type = Type.LOAD_MORE;

}

});

}

public boolean isLoadedAllNetData = false; //是否所有的数据都加载完成

@Override

protected boolean isEmpty(ListNetResultInfo result) {

if (result.getList().isEmpty()){

return true;

}

return false;

}

/**

* 样例代码,仅供参考

* @param state

* @param result

*/

@Override

protected void showData(BaseListModel.STATE state, ListNetResultInfo result) {

switch (state) {

case ASK_PRE:

break;

case ASK_ING:

break;

case ASK_ED:

break;

case ASK_ED_CANNOT_ACCESS:

case ASK_ED_FAIL:

case ASK_ED_ERROR:

if (mDataAdapter.getCount() == 0){

//显示虚拟布局

mDataAdapter.clear();

mMockData.state = state;

mDataAdapter.add(mMockData);

mSwipeRefreshHelper.setLoadMoreEnable(false);

mSwipeRefreshHelper.loadMoreComplete(false);

}

break;

case ASK_ED_EMPTY:

case ASK_ED_AVAILABILITY:

List list = result.getList();

for (int i = 0; i < list.size(); i++) {

mDataAdapter.add(list.get(i));

}

mDataAdapter.notifyDataSetChanged();

if (false){

isLoadedAllNetData = true;

mSwipeRefreshHelper.setNoMoreData();

}

else{

isLoadedAllNetData = false;

}

break;

}

switch (type) {

case REFRESH:

mSwipeRefreshHelper.refreshComplete();

mSwipeRefreshHelper.setLoadMoreEnable(true);

break;

case LOAD_MORE:

mSwipeRefreshHelper.loadMoreComplete(isLoadedAllNetData);

mSwipeRefreshHelper.setLoadMoreEnable(!isLoadedAllNetData);

break;

}

}

//不同的订单的适配器

public class DataAdapter extends ArrayAdapter {

public DataAdapter(Context context) {

super(context, getItemLayoutId(), getItemTextViewResourceId());

}

@Override

public View getView(int position, View convertView, ViewGroup parent) {

O item = getItem(position);

View result = bindView(position, super.getView(position, convertView, parent), parent);

switch (item.state){

case ASK_ED_CANNOT_ACCESS:

case ASK_ED_FAIL:

case ASK_ED_ERROR:

case ASK_ED_EMPTY:

result = getAbnormalView(item.state);

break;

case ASK_ED_AVAILABILITY:

}

return result;

}

@Override

public int getViewTypeCount() {

return BaseListModel.STATE.ASK_ED_AVAILABILITY.value+1;

}

@Override

public int getItemViewType(int position) {

O item = getItem(position);

return item.state.value;

}

}

TextView abnormalView = new TextView(mContext);

/**

* 返回异常状态下的显示布局

* @param state

* @return

*/

public View getAbnormalView(BaseListModel.STATE state) {

switch (state){

case ASK_ED_CANNOT_ACCESS:

abnormalView.setText("无法访问网络");

break;

case ASK_ED_FAIL:

abnormalView.setText("无法访问服务器");

break;

case ASK_ED_ERROR:

abnormalView.setText("请求参数错误");

break;

case ASK_ED_EMPTY:

abnormalView.setText("数据为空");

break;

}

return abnormalView;

}

/**

* 加载list数据

*

* @param startIndex 起始数据项

* @param pageSize 预期加载多少项

* @return

*/

protected abstract ListNetResultInfo onDoInBackgroundSafely(int startIndex, int pageSize);

/**

* 获取item布局中一个textview的id

*

* @return

*/

public abstract int getItemTextViewResourceId();

/**

* 获取对应Item布局的id

*

* @return

*/

public abstract int getItemLayoutId();

/**

* 将view和数据绑定

*

* @param position

* @param view

* @param parent

* @return

*/

public abstract View bindView(int position, View view, ViewGroup parent);

/**

* -------------------------

* END

* -------------------------

*/

}

Panda

2016-08-24

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值