美文网首页
记录一次修改刷新加载逻辑的思路

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

作者: Marker_Sky | 来源:发表于2018-05-24 16:56 被阅读0次

    一、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();
            }
        }
    }
    

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

    二、解决方案

    方案一(放弃):

    • 思路:将其它 VIew 都加载到 SuccessView 上,再根据状态进行展示。因为只有 SuccessView 上面有刷新和加载的控件。
    • 过程:将其它 View 放到 SuccessView 上无法显示。
      之后进行思考:每个 SuccessView 都是子 Fragment 独有的,如果这样做那么每个 Fragment 的 SuccessView 都要创建容器来装填其它 View,而且这个容器必须在刷新控件之中。
      这样做每个 Fragment 的布局都要或多或少进行修改,或许可通过代码添加布局容器,但要保证代码添加的容器必须在刷新控件中,比较麻烦。
      如果进行替换的话,其它 View 又不包含刷新控件,所以必须要替换到刷新控件的子 View 中才能实现刷新。
    • 总结:修改代码量较大,改动较多,无法统一管理,废弃。

    方案二(成功实现):

    • 思路:LoadingLayout 添加刷新加载控件,里面包含子 View,让所有状态 View 都添加到这个容器 View 上。这样各种状态 View 的父 View 都在刷新控件中,方便统一管理。
    方案二
    • 过程:
    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 貌似没有获取状态的函数。。。
    这样整个流程基本就结束了。

    • 总结:虽然功能实现了但还是存在一些问题,有些功能还需扩展和优化,比如实现子 Fragment 修改参数进行定制。

    方案三(建议):

    • 针对使用此框架的项目:使用大家的解决方案,进行优化。
    • 针对较新未使用此框架的项目:重新引入一个灵活性较高的刷新加载功能包,在需要使用的地方添加布局和进行控制即可。

    相关文章

      网友评论

          本文标题:记录一次修改刷新加载逻辑的思路

          本文链接:https://www.haomeiwen.com/subject/geyrjftx.html