RecyclerView 解析(三)

一、前言
RecyclerView 解析第三篇。
如果你对RecyclerView 没有一个基础的认识,那建议你去读前两篇博客。
第一篇博客主要是讲,RecyclerView 基本用法。RecyclerView (解析一)
第二篇博客将RecyclerView Item之间的拖动,以及滑动删除动画相关做了大概介绍。RecyclerView (解析二)
本篇博客想向各位介绍RecyclerView 相关库。
主要是常用的下拉刷新,上拉加载更多,及其原理。大家可以根据原理自己实现下拉刷新

,及上拉加载更多。当然还会介绍其他的一些库。不过相对的篇幅会小一些。

二、XRecyclerView 实现下拉刷新,上拉加载更多。

首先来看看效果:
正在刷新

基本用法:

1、 添加库 compile ‘com.jcodecraeer:xrecyclerview:1.2.7’

2、布局:

    <com.jcodecraeer.xrecyclerview.XRecyclerView
        android:layout_width="fill_parent"
        android:layout_height="fill_parent"
        android:id="@+id/recycler_view"
        />

利用com.jcodecraeer.xrecyclerview.XRecyclerView 替换 RecyclerView 。

3、代码

package recyclerdemo.li.com.recyclerdemo.refresh;

import android.app.Activity;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.view.LayoutInflater;
import android.view.View;

import com.jcodecraeer.xrecyclerview.ProgressStyle;
import com.jcodecraeer.xrecyclerview.XRecyclerView;

import java.util.ArrayList;
import java.util.List;

import recyclerdemo.li.com.recyclerdemo.R;
import recyclerdemo.li.com.recyclerdemo.common.CommonAdapter;
import recyclerdemo.li.com.recyclerdemo.common.ViewHolder;

public class RefreshActivity extends Activity {

    private List<String> mData;
    private Handler mHandler;

    private XRecyclerView mRecyclerView;
    private CommonAdapter mCommonAdapter;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        setContentView(R.layout.refresh_activity);
        mHandler = new Handler(){
            @Override
            public void handleMessage(Message msg) {
                super.handleMessage(msg);

                int i = msg.what;
                switch (i){
                    case 1:
                        mData.add(0,"refresh");
                        mRecyclerView.refreshComplete();
                        mCommonAdapter.notifyDataSetChanged();
                        break;
                    case 2:
                        mData.add("loadMore");
                        mRecyclerView.loadMoreComplete();
                        mCommonAdapter.notifyDataSetChanged();
                        break;
                    default:
                        break;
                }
            }
        };
        initData();
        initView();
    }

    public void initView(){
        mRecyclerView = (XRecyclerView)findViewById(R.id.recycler_view);
        mRecyclerView.setLayoutManager(new LinearLayoutManager(this));
        View view = LayoutInflater.from(this).inflate(R.layout.ultimate_header,mRecyclerView,false);
        mRecyclerView.addHeaderView(view);

        mCommonAdapter = new CommonAdapter(this,R.layout.recycler_item,mData) {
            @Override
            public void convert(RecyclerView.ViewHolder holder, Object o) {
                ViewHolder viewHolder = (ViewHolder)holder;
                viewHolder.setTextView(R.id.text,(String)o);
            }
        };
        mRecyclerView.setAdapter(mCommonAdapter);
        mRecyclerView.setPullRefreshEnabled(true);
        //设置刷新Style
        mRecyclerView.setRefreshProgressStyle(ProgressStyle.LineSpinFadeLoader);
        //设置上拉加载更多Style
//        mRecyclerView.setLoadingMoreProgressStyle(ProgressStyle.BallGridPulse);

        mRecyclerView.setLoadingListener(new XRecyclerView.LoadingListener() {
            @Override
            public void onRefresh() {
                refreshData();
            }

            @Override
            public void onLoadMore() {
                loadData();
            }
        });
    }

    private void initData(){
        if(mData == null){
            mData = new ArrayList<String>();
        }
        for(int i='A';i<='z';i++){
            mData.add(""+(char)i);
        }
    }

    private void refreshData(){

        new Thread(new Runnable() {
            @Override
            public void run() {
                try{
                    Thread.sleep(2000);
                    Message msg = Message.obtain();
                    msg.what = 1;
                    mHandler.sendMessage(msg);
                }catch (Exception e){

                }
            }
        }).start();

    }

    private void loadData(){
        new Thread(new Runnable() {
            @Override
            public void run() {
                try{
                    Thread.sleep(2000);
                    Message msg = Message.obtain();
                    msg.what = 2;
                    mHandler.sendMessage(msg);
                }catch (Exception e){

                }
            }
        }).start();
    }
}

View view = LayoutInflater.from(this).inflate(R.layout.ultimate_header,mRecyclerView,false);
mRecyclerView.addHeaderView(view);
利用mRecyclerView.addHeaderView(view);添加HeadView。集成者可以添加多个HeadView。

mCommonAdapter 为自定义Adapter。与本文无关,读者可以自己参照RecyclerView 第一篇博客自己实现。

mRecyclerView.setPullRefreshEnabled(true);设置刷新可用。XRecyclerView 这个库默认即可刷新。不需要刷新时可以设置为False。

mRecyclerView.setRefreshProgressStyle(ProgressStyle.LineSpinFadeLoader);
mRecyclerView.setLoadingMoreProgressStyle(ProgressStyle.BallGridPulse);
设置下拉刷新及上拉加载更多不同的Style。这里XRecyclerView 默认提供了许多的Style。即库内置了许多不同的图片,用于下拉及上拉时显示。

mRecyclerView.setArrowImageView();
集成者可以通过这个方法设置自己想要的下拉刷新图片。

mRecyclerView.setLoadingListener()。下拉刷新以及上拉加载更多回调。注意该回调是在主线程中。所以想要做耗时操作需要在子线程中。

刷新成功后调用mRecyclerView.refreshComplete();通知已经成功。
上拉加载成功后调用mRecyclerView.loadMoreComplete();通知已经成功。

setEmptyView(); RecyclerView 没有像ListView一样提供setEmptyView()这样的方法。这个库为我们提供了这个方法。

4、这个库的用法相对简单,我们可以其实现原理。

XRecyclerView extends RecyclerView 。
XRecyclerView 继承自RecyclerView 。

private ArrayList mHeaderViews = new ArrayList<>();
private ArrayList mFootViews = new ArrayList<>();
其内部维护了两个HeadView FootView集合。

    private void init(Context context) {
        mContext = context;
        if (pullRefreshEnabled) {
            ArrowRefreshHeader refreshHeader = new ArrowRefreshHeader(mContext);
            mHeaderViews.add(0, refreshHeader);
            mRefreshHeader = refreshHeader;
            mRefreshHeader.setProgressStyle(mRefreshProgressStyle);
        }
        LoadingMoreFooter footView = new LoadingMoreFooter(mContext);
        footView.setProgressStyle(mLoadingMoreProgressStyle);
        addFootView(footView);
        mFootViews.get(0).setVisibility(GONE);
    }

其在构造方法中调用了init()方法。ArrowRefreshHeader 为自定义的下拉刷新的View。LoadingMoreFooter 为自定义上拉加载View。可以看到在初始化时,分别将已经实现好的下拉刷新View已经上拉加载View添加到集合中。

下面来看addHeaderView()方法。

    public void addHeaderView(View view) {
        if (pullRefreshEnabled && !(mHeaderViews.get(0) instanceof ArrowRefreshHeader)) {
            ArrowRefreshHeader refreshHeader = new ArrowRefreshHeader(mContext);
            mHeaderViews.add(0, refreshHeader);
            mRefreshHeader = refreshHeader;
            mRefreshHeader.setProgressStyle(mRefreshProgressStyle);
        }
        mHeaderViews.add(view);
        sHeaderTypes.add(HEADER_INIT_INDEX + mHeaderViews.size());
    }

可以看到addHeaderView()方法中将HeadView,和ArrowRefreshHeader 一样加入到mHeaderViews 集合中,并且ArrowRefreshHeader 永远是在第一位。

public void addFootView(final View view) {
        mFootViews.clear();
        mFootViews.add(view);
    }
``
调用addFootView()方法 ,将我们自定义的View加入到mFootViews中。可以看到mFootViews中永远只有一个Item。表示FootView只能为一个。所以如果我们调用了addFootView()方法,我们自定义的FootView将会替换系统在init()方法中加入的FootView。


下面我们一起看Adapter:

private class WrapAdapter extends RecyclerView.Adapter {

    private RecyclerView.Adapter adapter;

    private ArrayList<View> mHeaderViews;

    private ArrayList<View> mFootViews;

    private int headerPosition = 1;

    public WrapAdapter(ArrayList<View> headerViews, ArrayList<View> footViews, RecyclerView.Adapter adapter) {
        this.adapter = adapter;
        this.mHeaderViews = headerViews;
        this.mFootViews = footViews;
    }

    @Override
    public void onAttachedToRecyclerView(RecyclerView recyclerView) {
        super.onAttachedToRecyclerView(recyclerView);
        RecyclerView.LayoutManager manager = recyclerView.getLayoutManager();
        if (manager instanceof GridLayoutManager) {
            final GridLayoutManager gridManager = ((GridLayoutManager) manager);
            gridManager.setSpanSizeLookup(new GridLayoutManager.SpanSizeLookup() {
                @Override
                public int getSpanSize(int position) {
                    return (isHeader(position) || isFooter(position))
                            ? gridManager.getSpanCount() : 1;
                }
            });
        }
    }

    @Override
    public void onViewAttachedToWindow(RecyclerView.ViewHolder holder) {
        super.onViewAttachedToWindow(holder);
        ViewGroup.LayoutParams lp = holder.itemView.getLayoutParams();
        if (lp != null
                && lp instanceof StaggeredGridLayoutManager.LayoutParams
                && (isHeader(holder.getLayoutPosition()) || isFooter(holder.getLayoutPosition()))) {
            StaggeredGridLayoutManager.LayoutParams p = (StaggeredGridLayoutManager.LayoutParams) lp;
            p.setFullSpan(true);
        }
    }

    public boolean isHeader(int position) {
        return position >= 0 && position < mHeaderViews.size();
    }

    public boolean isContentHeader(int position) {
        return position >= 1 && position < mHeaderViews.size();
    }

    public boolean isFooter(int position) {
        return position < getItemCount() && position >= getItemCount() - mFootViews.size();
    }

    public boolean isRefreshHeader(int position) {
        return position == 0;
    }

    public int getHeadersCount() {
        return mHeaderViews.size();
    }

    public int getFootersCount() {
        return mFootViews.size();
    }

    @Override
    public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
        if (viewType == TYPE_REFRESH_HEADER) {
            mCurrentPosition++;
            return new SimpleViewHolder(mHeaderViews.get(0));
        } else if (isContentHeader(mCurrentPosition)) {
            if (viewType == sHeaderTypes.get(mCurrentPosition - 1)) {
                mCurrentPosition++;
                return new SimpleViewHolder(mHeaderViews.get(headerPosition++));
            }
        } else if (viewType == TYPE_FOOTER) {
            return new SimpleViewHolder(mFootViews.get(0));
        }
        return adapter.onCreateViewHolder(parent, viewType);
    }

    private int mCurrentPosition;

    @Override
    public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
        if (isHeader(position)) {
            return;
        }
        int adjPosition = position - getHeadersCount();
        int adapterCount;
        if (adapter != null) {
            adapterCount = adapter.getItemCount();
            if (adjPosition < adapterCount) {
                adapter.onBindViewHolder(holder, adjPosition);
                return;
            }
        }
    }

    @Override
    public int getItemCount() {
        if (adapter != null) {
            return getHeadersCount() + getFootersCount() + adapter.getItemCount();
        } else {
            return getHeadersCount() + getFootersCount();
        }
    }

    @Override
    public int getItemViewType(int position) {
        if (isRefreshHeader(position)) {
            return TYPE_REFRESH_HEADER;
        }
        if (isHeader(position)) {
            position = position - 1;
            return sHeaderTypes.get(position);
        }
        if (isFooter(position)) {
            return TYPE_FOOTER;
        }
        int adjPosition = position - getHeadersCount();
        int adapterCount;
        if (adapter != null) {
            adapterCount = adapter.getItemCount();
            if (adjPosition < adapterCount) {
                return adapter.getItemViewType(adjPosition);
            }
        }
        return TYPE_NORMAL;
    }

    @Override
    public long getItemId(int position) {
        if (adapter != null && position >= getHeadersCount()) {
            int adjPosition = position - getHeadersCount();
            int adapterCount = adapter.getItemCount();
            if (adjPosition < adapterCount) {
                return adapter.getItemId(adjPosition);
            }
        }
        return -1;
    }

    @Override
    public void unregisterAdapterDataObserver(AdapterDataObserver observer) {
        if (adapter != null) {
            adapter.unregisterAdapterDataObserver(observer);
        }
    }

    @Override
    public void registerAdapterDataObserver(AdapterDataObserver observer) {
        if (adapter != null) {
            adapter.registerAdapterDataObserver(observer);
        }
    }

    private class SimpleViewHolder extends RecyclerView.ViewHolder {
        public SimpleViewHolder(View itemView) {
            super(itemView);
        }
    }
}

“`
Adapter中有这样几个方法值得我们注意。
就可以把整个过程弄明白了。通过getItemViewType()方法根据position 判断是HeadView还是FootView或者是正常View。在onCreateViewHolder()方法根据Type,如果是第一个View,也就是我们下拉刷新看到的View。
或者是我们通过addHeadView()方法添加进去的View,那就从mHeaderViews集合中取值组装ViewHolder。
如果是FootView就从mFootViews取值,组装ViewHolder。
如果都不是那便调用我们在外面自定义的adapter的onCreateViewHolder()。
这里我就搞明白了,为什么它可以添加多个HeadView。原理就是内部维护了集合,无论添加多少HeadView,在onCreateViewHolder()生成对应的ViewHolder就行了。并且注意onBindViewHolder(),getItemCount()考虑到数量的情况就好了

其他的方法均比较好理解。这里值得注意的方法就是onAttachedToRecyclerView()方法。
就是通过onAttachedToRecyclerView(),判断即使是GridLayoutManager,设置有好几列的情况,如何能让HeadView以及FootView依然是自己占用一行。就是在这个方法中判断如果是

GridLayoutManager,返回当前这个View的SpanSize为1就好了。

OK,大致的情况就介绍好了。原理依然是利用getItemViewType()与ListView一样。

哦,忘记了项目地址:https://github.com/jianghejie/XRecyclerView

三、其他的库:

UltimateRecyclerView Github上面Star非常的多。
项目地址:https://github.com/cymcsg/UltimateRecyclerView
这个库本来我也是用了一下,实现了很多功能。不足之处就是作者给的Demo,写的是在天马行空,看一会就失去兴致了。使用起来也比较繁琐。其内部是对RecyclerView 进行了再次包装,

UltimateRecyclerView 是一个FrameLayout。其内部是拥有一个RecyclerView 的对象。以后有时间可能会再研究研究。

还有:
PullLoadMoreRecyclerView https://github.com/WuXiaolong/PullLoadMoreRecyclerView
HeaderAndFooterRecyclerView https://github.com/cundong/HeaderAndFooterRecyclerView
IRecyclerView https://github.com/Aspsine/IRecyclerView
等等

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值