一、前言
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
等等