Skip to content

Latest commit

 

History

History
182 lines (144 loc) · 6.91 KB

3.LceermWidget.md

File metadata and controls

182 lines (144 loc) · 6.91 KB

目的

目的:学习 LceermWidget 的使用。

内容

创建一个 widget 继承 LceermWidget,分别渲染 4中状态的页面,点击按钮重新加载数据, 上滑加载更多,下拉刷新。

简介

Lcee 本章第一节已经介绍过了,rm 分别表示 refresh、 more。 这是最常见的需求,虽然实现起来不难,但代码写多了之后就发现,大部分都是重复的代码,但让人头疼的是,要提取出来并不容易,幸好我们有了 mvvm 框架,这让抽象和封装状态变得容易多了。

实际上看到这里,肯定有大佬会提出一个问题:rm 状态实际上应该是属于 content 里面的内容,为什么要放到同一个层级呢?

实际上这个问题我也想了很久,从实现上来说,放在同一个层级确实方便很多,因为他们之间关系太过紧密,强行分开需要额外的代价。 但作为一个较真的程序员,实际上,本库单独提供了一个 RmWidget,下一节就会介绍用 RmWidget + LceeWidget 实现本节一样的功能。

效果

和上一节基本相同,只是增加了下拉刷新和加载更多。

准备

新建 UserLceermWidget,并复制本章第一节的 WidgetActivity,修改里面的 wiget 引用。

UserLceermWidget

新建 UserLceeWidget 继承 LceermWidget。 需要实现 renderLoadingrenderContentrenderEmptyrenderErroronLoadDataonLoadMoreonRefreshErroronRefreshEmptyonRefreshCompleteonLoadMoreErroronLoadMoreEmptyonLoadMoreComplete 等方法。

大致意思从方法名就可以知道。

lee

renderLoadingrenderEmptyrenderError 这 3 个简单的和第一节的一样,不再重复。

content

比第一节多加了一个 SwipeRefreshWidget

    private SwipeRefreshWidget srw;
    private SwipeRefreshLayout.OnRefreshListener refresh = () -> {
        if (!refresh()) {
            srw.refreshing(false);
        }
    };

    @Override
    protected Widget renderContent() {
        srw = new SwipeRefreshWidget(context, lifecycle,
            renderRecycler()
        )
            .onRefreshListener(refresh)
            .matchParent();
        return new FrameWidget(context, lifecycle,
            srw,
            renderFab()
        );
    }

onLoadData onLoadMore

获取数据和第一节及其类似,但这里为了标记没有更多数据了,加了一个变量 noMoreData,这个并不是必要的,这里只是因为数据源的数据都是随机的,所以不得不这么做。

    private List<UserBean> data = Collections.emptyList();
    private boolean noMoreData;

    @Override
    protected LceeStatus onLoadData() throws NetworkErrorException {
        noMoreData = false;
        data = UserDataSource.getInstance().getUsersFromRemote();
        if (data.isEmpty())
            return LceeStatus.Empty;
        return LceeStatus.Content;
    }

    @Override
    protected LceeStatus onLoadMore() throws NetworkErrorException {
        data = UserDataSource.getInstance().getUsersFromRemote();
        if (data.isEmpty()) {
            noMoreData = true;
            return LceeStatus.Empty;
        }
        return LceeStatus.Content;
    }

onRefreshError onLoadMoreError

刷新和加载更多 也难免会失败,但失败之后,一般来说并不需要重新渲染一个新的页面,通常是弹出一个提示,所以这里封装的 api 也是这么考虑的。

    @Override
    protected void onRefreshError(Throwable throwable) {
        Toast.makeText(context, "Refresh failed !", Toast.LENGTH_SHORT).show();
        lastError.printStackTrace();
    }

    @Override
    protected void onRefreshComplete() {
        srw.refreshing(false);
    }

    @Override
    protected void onLoadMoreError(Throwable throwable) {
        Toast.makeText(context, "Load more data failed !", Toast.LENGTH_SHORT).show();
        lastError.printStackTrace();
    }

    @Override
    protected void onLoadMoreEmpty() {
        Toast.makeText(context, "No more data !", Toast.LENGTH_SHORT).show();
    }

    @Override
    protected void onLoadMoreComplete() {
        // change load more UI if necessary
    }

加载更多

下拉刷新是通过 SwipeRefreshLayout 来实现的,而加载更多官方并没有提供这么便利的东西,出于让本 demo 尽量简单少依赖,直接复制粘贴了网上一个简单的实现,虽然简陋,但能用。 各位开发者采用的实现方式各不相同,你需要关心的时候如何触发加载更多。 实际上很简单,调用方法 loadMore() 即可。

更新数据时,你需要判断 loadType 来决定是 setData 还是 addData

    private RecyclerWidget renderRecycler() {
        return new RecyclerWidget<UserItemAdapter>(context, lifecycle) {
            @Override
            protected void initProps() {
                width = matchParent;
                height = matchParent;
                layoutManager = new LinearLayoutManager(context);
                adapter = new UserItemAdapter(lifecycle);

                // load more
                view.addOnScrollListener(new RecyclerView.OnScrollListener() {
                    private int mLastVisibleItemPosition;
                    @Override
                    public void onScrollStateChanged(RecyclerView recyclerView, int newState) {
                        if (adapter == null || noMoreData || status == LceeStatus.Loading)
                            return;
                        RecyclerView.LayoutManager layoutManager = recyclerView.getLayoutManager();
                        if (layoutManager instanceof LinearLayoutManager) {
                            mLastVisibleItemPosition = ((LinearLayoutManager) layoutManager).findLastVisibleItemPosition();
                        }
                        if (newState == RecyclerView.SCROLL_STATE_IDLE
                                && mLastVisibleItemPosition + 1 == adapter.getItemCount()) {
                            loadMore();
                        }
                    }
                });
            }

            @Override
            public void update() {
                super.update();
                // attention: must check status before update view data
                if (status != LceeStatus.Content)
                    return;
                switch (loadType) {
                    case FirstLoad:
                    case Refresh:
                        adapter.setData(data);
                        break;
                    case LoadMore:
                        adapter.addData(data);
                        break;
                }
            }
        };
    }

总结

除了新增的 LoadType,其他和第一节基本一样。 在这样高度封装之后,大大减少了状态管理的重复代码,让你可以专注于业务,同时也可以提高代码可读性。