优雅地实现RecyclerView的上拉加载

RecylerView 上拉加载更多 上拉加载的多状态


这篇博客是承接上一篇博客--探索Android架构的DataLayer层(DataManager方式)具体实现,其实是上篇博客的一个使用比较普遍的例子,当然如果把上一篇博客设计的数据加载回调接口提炼出来也是可以做一篇单独的文章。

先说说我们希望的RecycerView应该有的样子:上拉加载更多,没有更多,加载错误然后点击重试。至于通常的下拉刷新我同意Google设计下拉刷新的理念,即下拉刷新是View的行为,应该让View自己来实现,而下拉刷新有着更加广泛的应用,几乎需要交互的信息显示界面都是需要下拉刷新功能的。所以RecyclerView的下拉刷新应该由她的父空间来实现,有过有这个需求的话,因为也不是一定需要下拉刷新。这里下拉刷新控件我推荐秋百万的下拉刷新控件,6k+的star,值得信赖。

先谈谈思路,其实很简单,就是通过getItemViewType()加载不同的布局,这里就是把加载更多布局封装进adapter中。
接着上篇写的数据加载接口来看,开始加载的时候加入加载的itemView,完成加载后去掉它。通过给RecyclerView添加的滑动事件来判断加载时机。先看看上篇博客写的数据加载接口吧:

public interface DataLoadingSubject {
    boolean isDataLoading();
    void registerCallback(DataLoadingCallbacks callBack);
    void unregistereCallBack(DataLoadingCallbacks callBack);
    interface DataLoadingCallbacks{
        void dataStartedLoading();
        void dataFinishedLoading();
    }
}

接下来我们定义加载更多的itemViewType的值是-1,直接让adpater实现上面这个接口,这里直接贴出adapter的代码:

public class ArticleAdapter extends RecyclerView.Adapter
        implements DataLoadingSubject.DataLoadingCallbacks {

    private static final int TYPE_LOADING_MORE = -1;
    private static final int TYPE_ARTICLE_T1 = 0;
    //用来加载更多视图样板
    //private static final int TYPE_ARTICLE_T2 = 1;
    //private static final int TYPE_ARTICLE_T3 = 2;

    private List
mArticles; private Activity mActivity; private LayoutInflater mLayoutInflater; private DataLoadingSubject dataLoading; private boolean showLoadingMore = false; public ArticleAdapter(Activity activity, DataLoadingSubject dataLoading) { this.mActivity = activity; this.dataLoading = dataLoading; dataLoading.registerCallback(this); mLayoutInflater = mActivity.getLayoutInflater(); mArticles = new ArrayList<>(); } @Override public int getItemViewType(int position) { if (position < getDataItemCount() && getDataItemCount() > 0) { return TYPE_ARTICLE_T1; } return TYPE_LOADING_MORE; } @Override public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { switch (viewType){ case TYPE_LOADING_MORE: return new LoadingMoreHolder(mLayoutInflater.inflate(R.layout.infinite_loading,parent,false)); case TYPE_ARTICLE_T1: return new ArticleT1Holder(mLayoutInflater.inflate(R.layout.item_articles_t1,parent,false)); } return null; } @Override public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) { switch (getItemViewType(position)){ case TYPE_LOADING_MORE: bindLoadingViewHolder((LoadingMoreHolder) holder,position); break; case TYPE_ARTICLE_T1: bindActicleT1Holder(getItem(position), (ArticleT1Holder) holder); break; } } private Article getItem(int position){ return mArticles.get(position); } public int getDataItemCount() { return mArticles.size(); } @Override public int getItemCount() { return getDataItemCount() + (showLoadingMore ? 1 : 0); } @Override public void dataStartedLoading() { if (showLoadingMore) return; showLoadingMore = true; notifyItemInserted(getLoadingMoreItemPosition()); } @Override public void dataFinishedLoading() { if (showLoadingMore) return; int loadingPos = getLoadingMoreItemPosition(); showLoadingMore = false; notifyItemRemoved(loadingPos); } private void bindActicleT1Holder(Article article,ArticleT1Holder holder){ Glide.with(mActivity) .load(article.HeadImgId) .diskCacheStrategy(DiskCacheStrategy.RESULT) .fitCenter() .into(holder.image); holder.abstractTv.setText(article.Abstract); holder.titleTv.setText(article.Title); } private void bindLoadingViewHolder(LoadingMoreHolder hodler, int position) { hodler.mProgressBar.setVisibility((position > 0 && dataLoading.isDataLoading()) ? View.VISIBLE : View.INVISIBLE); } public int getLoadingMoreItemPosition() { return showLoadingMore ? getItemCount() - 1 : RecyclerView.NO_POSITION; } static class ArticleT1Holder extends RecyclerView.ViewHolder { @BindView(R.id.foregounde_iv) ImageView image; @BindView(R.id.title_tv) TextView titleTv; @BindView(R.id.abstract_tv) TextView abstractTv; public ArticleT1Holder(View itemView) { super(itemView); ButterKnife.bind(this, itemView); } } static class LoadingMoreHolder extends RecyclerView.ViewHolder { ProgressBar mProgressBar; public LoadingMoreHolder(View itemView) { super(itemView); mProgressBar = (ProgressBar) itemView; } } }

然后我们开始判断这个滑动到底的时候加载更多,这个时候我们给我们的RecyclerView添加一个滑动事件监听事件:

public abstract class InfiniteScrollListener extends RecyclerView.OnScrollListener {

    // 底部还剩下几个的时候开始触发加载更多的回调接口
    private static final int VISIBLE_THRESHOLD = 5;

    private final LinearLayoutManager layoutManager;
    private final DataLoadingSubject dataLoading;

    public InfiniteScrollListener(@NonNull LinearLayoutManager layoutManager,
                                  @NonNull DataLoadingSubject dataLoading) {
        this.layoutManager = layoutManager;
        this.dataLoading = dataLoading;
    }

    @Override
    public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
        // bail out if scrolling upward or already loading data
        if (dy < 0 || dataLoading.isDataLoading()) return;

        final int visibleItemCount = recyclerView.getChildCount();
        final int totalItemCount = layoutManager.getItemCount();
        final int firstVisibleItem = layoutManager.findFirstVisibleItemPosition();

        if ((totalItemCount - visibleItemCount) <= (firstVisibleItem + VISIBLE_THRESHOLD)) {
            onLoadMore();
        }
    }

    public abstract void onLoadMore();

}

最后当然是在presenter或者activity中如何使用,我今天写的这个其实是上一篇博客的继续或者例子。

mDataManager = new ArticleDataManager(this, "870a9973-e657-401d-a12b-0da036a29583") {
            @Override
            public void onDataLoaded(List
data) { mAdapter.addDataSet(data); } }; mAdapter = new ArticleAdapter(this, mDataManager); mRecyclerView.setAdapter(mAdapter); final LinearLayoutManager layoutManager = new LinearLayoutManager(this); mRecyclerView.setLayoutManager(layoutManager); mRecyclerView.addOnScrollListener(new InfiniteScrollListener(layoutManager, mDataManager) { @Override public void onLoadMore() { mDataManager.loadData(); } }); mDataManager.loadData();

OK,因为网速太好经常看不到那个加载视图就不截图了,需要的自己试试吧,原理也不复杂,不用框架自己尝试也是蛮好的。

你可能感兴趣的:(优雅地实现RecyclerView的上拉加载)