记录一次修改刷新加载逻辑的思路

2018-05-24  本文已影响0人  Marker_Sky

一、View 显示流程

  1. 创建 LoadingLayout,它是一个 FrameLayout,添加了四种状态的 View 显示:加载(mLoadingView)、错误(mErrorView)、空(mEmptyView)和成功(mSucceedView)。
LoadingLayout结构图

BaseLoadingFragment # onCreateView

@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, final Bundle savedInstanceState) {
    //每次ViewPager要展示该页面时,均会调用该方法获取显示的View
    LogUtils.i(TAG, " fragment on create view");
    if (mContentView == null) {//为null时,创建一个
        // 每次ViewPager要展示该页面时,均会调用该方法获取显示的View
        LogUtils.i(TAG, " fragment on create loading layout");
        mContentView = new LoadingLayout(UIUtils.getContext()) {
          ...
        }
        initView(mContentView.getSucceedView());
        initCreated(savedInstanceState);
    } else {
        // 不为null时,需要把自身从父布局中移除,因为ViewPager会再次添加
        ViewUtils.removeSelfFromParent(mContentView);
    }
    return mContentView;
}

LoadingLayout # init()

public LoadingLayout(Context context) {
    super(context);
    init();
}

private void init() {
    mState = STATE_UNLOADED;//初始化状态
    // 创建对应的View,并添加到布局中
    // 该方法其实是加载一个布局
    mLoadingView = createLoadingView();
    if (mLoadingView != null) {
        addView(mLoadingView, new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT));
    }
    // 同理,将其它两个布局加载并添加到当前 View
    mErrorView = createErrorView();
    ...
    mEmptyView = createEmptyView();
    ...
    // SucceedView 的实例化与上面三个 View 不同
    mSucceedView = createSucceedView();
    ...
}
protected View createLoadingView() {
    return UIUtils.inflate(R.layout.loading_page_loading);
}

mSucceedView 的创建需要具体实现

public abstract View createSucceedView();
  1. 创建 mSucceedView。创建 LoadingLayout 时需要实现该抽象方法创建 mSucceedView。

BaseLoadingFragment # onCreateView()

mContentView = new LoadingLayout(UIUtils.getContext()) {
      ...
    @Override
    public View createSucceedView() {
        LogUtils.i(TAG, "loading view create");
        return BaseLoadingFragment.this.createSucceedView();
    }
}

BaseLoadingFragment # createSucceedView() ==> BaseFragment # setContentView

// 加载完成的View
protected View createSucceedView() {
    return setContentView();
}
protected abstract View setContentView();

该抽象方法需要子 Fragment 实现,从而通过子 Fragment 的 setContentView 方法创建各自的 SuccessView。

举例:

/**
 * 类名称:NotArriveOrderListFragment
 * 类功能:待送达列表
 */
public class NotArriveOrderListFragment extends BaseLoadingFragment {
    ...
    @Override
    protected View setContentView() {
        return UIUtils.inflate(getActivity(), R.layout.fragment_not_arrive_order);
    }
    ...
}

该子 Fragment 在 setContentView() 中加载了自己的布局,并作为 mSuccessView 返回给 LoadingLayout 进行管理。

  1. LoadingLayout 根据数据加载的不同状态来控制相关 View 的展示。
根据状态控制显示
/**
 * 获取待送达列表的返回信息
 */
public CHttpTask mNotArriveOrderListTask = new CHttpTask<NotArriveOrderListReq, NotArriveOrderListRes>() {
    @Override
    public void onTrueMsg(NotArriveOrderListReq request, NotArriveOrderListRes response) {
        ...
        if (0 == mActivity.mUserInfo.workStatus) {
            // 获取到的数据为空
            if (ListUtils.isEmpty(mRowsList)) {
                // 调用 show 方法展示 EMPTY 页面,隐藏其它页面
                show(LoadingLayout.LoadResult.EMPTY);
                return;
            }
            // 根据数据状态显示不同 View
            show(check(mNotArriveOrderAdapter.getData()));
        }else {
            // 数据获取成功展示 SUCCEED 页面
            show(LoadingLayout.LoadResult.SUCCEED);
            ...
        }
    }
    ...
};

show() 方法会调用 LoadingLayout(View 管理者)提供的 show() 方法。由于上面例子都是带参数的,所以只分析带参数的show()方法。

LoadingLayout # show()

public synchronized void show(LoadResult result) {
    mState = result.getValue();
    showPageSafe();
    ...
}

这里 mState 已经获取到值了,也就获取到了当前的状态。接下来根据相应状态展示相关 View,隐藏无用 View。

/**
 * 显示对应的View
 */
private void showPage() {
    if (mLoadingView != null) {
        mLoadingView.setVisibility(mState == STATE_UNLOADED || mState == STATE_LOADING ? View.VISIBLE : View.INVISIBLE);
    }
    if (mErrorView != null) {
        mErrorView.setVisibility(mState == STATE_ERROR ? View.VISIBLE : View.INVISIBLE);
    }
    if (mEmptyView != null) {
        mEmptyView.setVisibility(mState == STATE_EMPTY ? View.VISIBLE : View.INVISIBLE);
    }
    if (mSucceedView != null) {
        mSucceedView.setVisibility(mState == STATE_SUCCEED ? View.VISIBLE : View.INVISIBLE);
        if (mState == STATE_SUCCEED) {
            loadViewSuccess();
        }
    }
}

到这里需要了解的流程就差不多走完了,接下来就是解决方案。

二、解决方案

方案一(放弃):

方案二(成功实现):

方案二
  1. LoadingLayout 创建时加载布局文件,里面包含刷新控件和容器 View。
private void init() {
    ...
    View mPView = createParentView();
    // 先把加载的布局 View 添加进来
    addView(mPView);
    // 找到容器 View
    mParentView = (PullFrameLayout) mPView.findViewById(R.id.fl_container);
    // 把各种状态 View 添加到容器 View上
    if (mLoadingView != null) {
        mParentView.addView(mLoadingView, new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT));
    }
    ...
    mSucceedView = createSucceedView();
    if (mSucceedView != null) {
        mParentView.addView(mSucceedView, new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT));
    }

}
protected View createParentView(){
    return UIUtils.inflate(R.layout.parent_view);
}
  1. 添加成功后就可以进行刷新和加载了。
mPullRefreshLayout = (PullRefreshLayout) mPView.findViewById(R.id.pr_pull);
mPullRefreshLayout.setOnRefreshListener(mRefreshListener);

private PullRefreshLayout.OnRefreshListener mRefreshListener = new PullRefreshLayout.OnRefreshListener() {
    @Override
    public void onRefresh(PullRefreshLayout pullRefreshLayout) {
        loadData(true);
    }

    @Override
    public void onLoadMore(PullRefreshLayout pullRefreshLayout) {
        loadData(false);
    }
};

public abstract void loadData(boolean isRefresh);

创建抽象方法传递刷新加载标记,以这种方式通知 Fragment 进行刷新和加载的操作。

  1. 子 Fragment 必须实现 loadData() 方法来执行刷新和加载操作。
    举例:

NotArriveOrderListFragment # loadData()

@Override
protected void loadData(boolean isRefresh) {
    if(isRefresh){
        isLoadMore = false;
        mCurrentPage = 1;
        getNotArriveOrderListByHttp(mCurrentPage);
    } else {
        if (mCurrentPage < mPages) {
            getNotArriveOrderListByHttp(++mCurrentPage);
        } else {
            UIUtils.showToastSafe(getResources().getString(R.string.not_more_data));
            mContentView.setLoadMoreFinish();
        }
    }
}

当 mPullRefreshLayout 执行刷新和加载时会调用到具体的方法,子 Fragment 执行具体逻辑进行网络请求。

  1. 请求成功或失败时,停止 mPullRefreshLayout 的刷新加载操作。
public CHttpTask mNotArriveOrderListTask = new CHttpTask<NotArriveOrderListReq, NotArriveOrderListRes>() {
    @Override
    public void onTrueMsg(NotArriveOrderListReq request, NotArriveOrderListRes response) {
        mContentView.setPullFinish();
        ...
    }
}

mContentView 就是 LoadingLayout,提供停止刷新和加载的操作:

LoadingLayout # setPullFinish()

public void setPullFinish(){
    UIUtils.runOnMainThreadDelay(new Runnable() {
        @Override
        public void run() {
            mPullRefreshLayout.refreshFinish(PullRefreshLayout.SUCCEED);
            mPullRefreshLayout.loadMoreFinish(PullRefreshLayout.SUCCEED);
        }
    },1000);
}

这里应根据 mPullRefreshLayout 的状态来确定是否停止刷新或加载,但是这个 mPullRefreshLayout 貌似没有获取状态的函数。。。
这样整个流程基本就结束了。

方案三(建议):

上一篇下一篇

猜你喜欢

热点阅读