RecyclerViewHelper是RecyclerView 的工具类,可以更方便的实现 Adapter,item 点击事件,更快的实现加载提示,分页加载。
首先我们来看看demo
先是MainActivity
private RecyclerView list_container;
private List<String> dataList;
private ListAdapter listAdapter;
private RecyclerViewHelper recyclerViewHelper;
private int loadCount = 0;
定义了五个变量,分别是 RecyclerView ,String泛型的List,自定义List适配器,本文所提及的Helper,和一个计数器loadCount
我们先看看适配器ListAdapter
public class ListAdapter extends CommonAdapter<String> {
public ListAdapter(@NonNull List<String> dataList) {
super(dataList, R.layout.item_list);
}
@Override
public void convert(BaseViewHolder holder, String s, int position) {
}
}
代码很简单,继承了一个CommonAdapter,设定了一个item布局,但需要注意的是这里所继承的CommonAdapter是重写的
我们来看看源码
public abstract class CommonAdapter<T> extends RecyclerView.Adapter<BaseViewHolder>
这是一个抽象类继承于RecyclerView的Adapter
我们来看看整个类的代码
public abstract class CommonAdapter<T> extends RecyclerView.Adapter<BaseViewHolder> {
private List<T> dataList; //T泛型的List
private int itemLayoutId = 0; //item布局id
public CommonAdapter(@NonNull List<T> dataList, int itemLayoutId) { //构造方法,这里的NonNull指的是不为null的附加条件
this.dataList = dataList;
this.itemLayoutId = itemLayoutId;
}
@Override
public BaseViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { //Holder的onCreate方法,绑定了item布局id
BaseViewHolder holder = new BaseViewHolder(getLayoutView(parent, itemLayoutId));
setListener(holder);
return holder;
}
@Override
public void onBindViewHolder(BaseViewHolder holder, int position) { //绑定Holder
convert(holder, dataList.get(position), holder.getAdapterPosition());
}
@Override
public int getItemCount() { //获取item的数目
return dataList.size();
}
public View getLayoutView(ViewGroup parent, int layoutId) { //返回LayoutView
return LayoutInflater.from(parent.getContext()).inflate(layoutId, parent, false);
}
/**
* 设置
*
* @param viewHolder
*/
private void setListener(final BaseViewHolder viewHolder) { //设置监听,监听点击事件
viewHolder.getConvertView().setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
if (onItemClickListener != null) {
int position = viewHolder.getAdapterPosition(); //获取position
onItemClickListener.onItemClick(v, viewHolder, position);
}
}
});
}
public abstract void convert(BaseViewHolder holder, T t, int position); //抽象的convert方法
//点击监听部分源代码
private OnItemClickListener onItemClickListener;
public interface OnItemClickListener { //监听器接口
void onItemClick(View view, BaseViewHolder holder, int position);
}
public void setOnItemClickListener(OnItemClickListener onItemClickListener) {
this.onItemClickListener = onItemClickListener;
}
}
总而言之,CommonAdapter是夹带设置监听点击事件的Adapter,以前的话监听点击是需要我们自己手动添加监听在Adapter中,有了这个之后变得简单了。
需要注意的是这里的BaseViewHolder也是重写的
贴一下代码
public class BaseViewHolder extends RecyclerView.ViewHolder {
private final SparseArray<View> views; //SparseArray,之前的文章有提及,一种效率高的Array,这里使用View泛型
public View convertView;
public BaseViewHolder(View itemView) {
super(itemView);
this.views = new SparseArray<>();
convertView = itemView;
}
public View getConvertView() {
return convertView;
}
public <T extends View> T getView(int viewId) {//返回相应泛型的View
View view = views.get(viewId);
if (view == null) {
view = convertView.findViewById(viewId);
views.put(viewId, view);
}
return (T) view;
}
....
}
下面给出MainActvity的代码
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);//设置布局
list_container = (RecyclerView) findViewById(R.id.list_container); //实例化RecyclerView
dataList = new ArrayList<>();
listAdapter = new ListAdapter(dataList); //设置适配器
//使用helper实现分页加载和加载的Tips
recyclerViewHelper = new RecyclerViewHelper(list_container, listAdapter);
//设置没有数据的Tips
recyclerViewHelper.setTipsEmptyView(R.layout.view_data_empty);
//设置加载中的Tips
recyclerViewHelper.setTipsLoadingView(R.layout.view_data_loading);
//设置加载失败的Tips
recyclerViewHelper.setTipsErrorView(R.layout.view_data_error);
//加载失败,没有数据时Tips的接口
recyclerViewHelper.setTipsListener(new TipsListener() {
@Override
public void retry() {
initData();
}
});
//设置header
// recyclerViewHelper.setHeaderView(R.layout.view_header);
//加载更多的接口
recyclerViewHelper.setLoadMoreListener(new LoadMoreListener() {
@Override
public void loadMore() {
loadNext();
}
});
initData();
}
给出loadNext和initData方法的代码
private void loadNext() {
new Thread(new Runnable() {
@Override
public void run() {
try {
Thread.sleep(800);
} catch (InterruptedException e) {
e.printStackTrace();
}
runOnUiThread(new Runnable() {
@Override
public void run() {
if (loadCount % 2 != 0) {
//分页数据加载失败
recyclerViewHelper.loadMoreError();
} else if (loadCount < 7) {
for (int i = 0; i < 10; i++) {
dataList.add(String.valueOf(i));
}
//分页数据加载成功,还有下一页
recyclerViewHelper.loadMoreFinish(true);
} else {
//分页数据加载成功,没有更多。即全部加载完成
recyclerViewHelper.loadMoreFinish(false);
}
loadCount++;
}
});
}
}).start();
}
private void initData() {
dataList.clear();
new Thread(new Runnable() {
@Override
public void run() {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
runOnUiThread(new Runnable() {
@Override
public void run() {
if (loadCount == 0) {
//首次加载数据成功
recyclerViewHelper.loadTipsComplete();
} else if (loadCount == 1) {
//首次数据记载失败
recyclerViewHelper.loadTipsError();
} else {
for (int i = 0; i < 10; i++) {
dataList.add(String.valueOf(i));
}
recyclerViewHelper.loadTipsComplete();
}
loadCount++;
}
});
}
}).start();
}
到此,已基本实现RecyclerView适配器的加载,可以看出这个H
elper可以帮助我们更好的实现Adapter的加载,各种加载提示和分页加载
接下来我们来看看这个Helper的源码
public class RecyclerViewHelper {
private RecyclerView recyclerView; //RecyclerView
private RecyclerView.LayoutManager layoutManager; //布局管理器
private RecyclerView.Adapter adapter; //适配器
private HelperAdapter helperAdapter; //自定义适配器
private LoadMoreListener loadMoreListener; //自定义监听器
private boolean hasMore = true; //是否有更多
private boolean isLoading = false; //是否正在加载
//构造方法1
public RecyclerViewHelper(RecyclerView recyclerView, RecyclerView.Adapter adapter) {
this(recyclerView, null, adapter);
}
//构造方法2
public RecyclerViewHelper(@NonNull RecyclerView recyclerView, RecyclerView.LayoutManager layoutManager,
@NonNull RecyclerView.Adapter adapter) {
this.recyclerView = recyclerView;
this.layoutManager = layoutManager;
this.adapter = adapter;
if (layoutManager == null) {//不传layoutManager默认为LinearLayoutManager
this.layoutManager = new LinearLayoutManager(recyclerView.getContext());
} else {
this.layoutManager = layoutManager;
}
setup();
}
private void setup() {
recyclerView.setLayoutManager(layoutManager);
recyclerView.setHasFixedSize(true);//设置item固定宽高可提高性能
helperAdapter = new HelperAdapter(adapter);
recyclerView.setAdapter(helperAdapter);
//监听RecyclerView的滚动
recyclerView.addOnScrollListener(new RecyclerViewScrollListener() {
@Override
public void loadMore(RecyclerView recyclerView) {
if (isLoading || adapter.getItemCount() == 0)
return;
if (hasMore) {//设置footer为加载中...
if (loadMoreListener != null) {
LoadMoreUtil.updateState(recyclerView, LoadMoreView.State.LOADING, null);
isLoading = true;
loadMoreListener.loadMore();
}
} else {//设置footer为加载完成
LoadMoreUtil.updateState(recyclerView, LoadMoreView.State.NO_MORE, null);
}
}
});
}
//设置tips为空的view
public RecyclerViewHelper setTipsEmptyView(int layoutId) {
if (helperAdapter != null) {
helperAdapter.setTipsEmptyView(layoutId);
}
return this;
}
//设置正在加载的View
public RecyclerViewHelper setTipsLoadingView(int layoutId) {
if (helperAdapter != null) {
helperAdapter.setTipsLoadingView(layoutId);
}
return this;
}
//设置错误提示的View
public RecyclerViewHelper setTipsErrorView(int layoutId) {
if (helperAdapter != null) {
helperAdapter.setTipsErrorView(layoutId);
}
return this;
}
//设置顶部View
public RecyclerViewHelper setHeaderView(int layoutId) {
if (helperAdapter != null) {
helperAdapter.setHeaderView(layoutId);
}
return this;
}
//加载完成
public void loadTipsComplete() {
if (helperAdapter != null) {
helperAdapter.loadTipsComplete();
}
}
/**
* 加载失败
*/
public void loadTipsError() {
if (helperAdapter != null) {
helperAdapter.loadTipsError();
}
}
//加载更多完成
public void loadMoreFinish(boolean hasMore) {
this.hasMore = hasMore;
this.isLoading = false;
if (!hasMore) {
LoadMoreUtil.updateState(recyclerView, LoadMoreView.State.NO_MORE, null);
}
if (helperAdapter != null) {
helperAdapter.notifyDataSetChanged(); //设置改变
}
}
public void loadMoreError() {//设置footer为加载失败
this.hasMore = true;
this.isLoading = false;
LoadMoreUtil.updateState(recyclerView, LoadMoreView.State.ERROR, new View.OnClickListener() {
@Override
public void onClick(View v) {
LoadMoreUtil.updateState(recyclerView, LoadMoreView.State.LOADING, null);
if (loadMoreListener != null) {
loadMoreListener.loadMore();
}
}
});
}
public void setLoadMoreListener(LoadMoreListener loadMoreListener) {
this.loadMoreListener = loadMoreListener;
}
//设置监听
public void setTipsListener(TipsListener tipsListener) {
if (helperAdapter != null) {
helperAdapter.setTipsListener(tipsListener);
}
}
}
可以看出添加了很多提示方面的View
我们来看一看HelperAdapter
public class HelperAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> { //继承于RecyclerView的Adapter泛型是ViewHolder
private static final String TAG = HelperAdapter.class.getSimpleName();
//设置各情况的标志码
public static final int HEADER_VIEW = 0x00000111;
public static final int FOOTER_VIEW = 0x00000222;
public static final int LOADING_VIEW = 0x00000333;
public static final int EMPTY_VIEW = 0x00000444;
public static final int ERROR_VIEW = 0x00000555;
/**
* 第一次加载提示状态
*/
public enum Tips {
LOADING, EMPTY, ERROR, NORMAL
}
private Tips tips = Tips.LOADING;
//各类Tips布局ID
private int headerLayoutId = 0;
private int emptyLayoutId = 0;
private int loadingLayoutId = 0;
private int errorLayoutId = 0;
//footerView
private View footerView;
//设置适配器
private final RecyclerView.Adapter<RecyclerView.ViewHolder> itemAdapter;
public HelperAdapter(@NonNull RecyclerView.Adapter itemAdapter) {
this.itemAdapter = itemAdapter;
}
/**
* 添加Header
*
* @param headerLayout
*/
public void setHeaderView(int headerLayout) {
this.headerLayoutId = headerLayout;
}
/**
* 添加Footer
*
* @param footerView
*/
public void setFooterView(View footerView) {
this.footerView = footerView;
}
public View getFooterView() {
return footerView;
}
/**
* 设置空数据显示view
*
* @param emptyLayout
*/
public void setTipsEmptyView(int emptyLayout) {
this.emptyLayoutId = emptyLayout;
}
/**
* 设置第一次加载时的view
*
* @param loadingLayout
*/
public void setTipsLoadingView(int loadingLayout) {
this.loadingLayoutId = loadingLayout;
}
/**
* 设置第一次加载错误view
*
* @param errorLayout
*/
public void setTipsErrorView(int errorLayout) {
this.errorLayoutId = errorLayout;
}
/**
* 加载完成
*/
public void loadTipsComplete() {
if (itemAdapter.getItemCount() > 0) {
tips = Tips.NORMAL;
} else {
tips = Tips.EMPTY;
}
notifyDataSetChanged();
}
/**
* 加载失败
*/
public void loadTipsError() {
if (itemAdapter.getItemCount() == 0) {
tips = Tips.ERROR;
}
notifyDataSetChanged();
}
/**
* 第一次加载重试
*/
public void loadTipsRetry() {
if (itemAdapter.getItemCount() == 0) {
tips = Tips.LOADING;
}
notifyDataSetChanged();
}
private int getHeaderViewCount() {
return getViewCount(headerLayoutId);
}
private int getFooterViewCount() {
return footerView == null ? 0 : 1;
}
private int getEmptyViewCount() {
return getViewCount(emptyLayoutId);
}
private int getLoadingViewCount() {
return getViewCount(loadingLayoutId);
}
private int getErrorViewCount() {
return getViewCount(errorLayoutId);
}
private int getViewCount(int layoutId) {
return layoutId > 0 ? 1 : 0;
}
public View getLayoutView(ViewGroup parent, int layoutId) {
return LayoutInflater.from(parent.getContext()).inflate(layoutId, parent, false);
}
/**
* 没有数据的view显示逻辑
*
* @return
*/
public int getEmptyViewType() {
if (tips == Tips.LOADING && getLoadingViewCount() > 0) {
return LOADING_VIEW;
} else if (tips == Tips.ERROR && getErrorViewCount() > 0) {
return ERROR_VIEW;
} else {
return EMPTY_VIEW;
}
}
/**
* 获取显示类型
*
* @param position
* @return
*/
@Override
public int getItemViewType(int position) {
if (getHeaderViewCount() == 0) {
if (itemAdapter.getItemCount() == 0 && getEmptyViewCount() > 0) {
return getEmptyViewType();
}
} else {
if (position == 0) {
return HEADER_VIEW;
} else if (position == 1 && itemAdapter.getItemCount() == 0 && getEmptyViewCount() > 0) {
return getEmptyViewType();
}
}
if (position >= (getHeaderViewCount() + itemAdapter.getItemCount())) {
return FOOTER_VIEW;
}
return super.getItemViewType(position - getHeaderViewCount());
}
/**
* 加载holder显示布局
*
* @param parent
* @param viewType
* @return
*/
@Override
public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
if (viewType == LOADING_VIEW) {
return new BaseViewHolder(getLayoutView(parent, loadingLayoutId));
} else if (viewType == ERROR_VIEW) {
BaseViewHolder holder = new BaseViewHolder(getLayoutView(parent, errorLayoutId));
if (tipsListener != null) {
holder.getConvertView().setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
loadTipsRetry();
tipsListener.retry();
}
});
}
return holder;
} else if (viewType == EMPTY_VIEW) {
BaseViewHolder holder = new BaseViewHolder(getLayoutView(parent, emptyLayoutId));
if (tipsListener != null) {
holder.getConvertView().setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
loadTipsRetry();
tipsListener.retry();
}
});
}
return holder;
} else if (viewType == HEADER_VIEW) {
return new BaseViewHolder(getLayoutView(parent, headerLayoutId));
} else if (viewType == FOOTER_VIEW) {
return new BaseViewHolder(footerView);
} else {
return itemAdapter.onCreateViewHolder(parent, viewType);
}
}
/**
* 将数据绑定到布局上
*
* @param holder
* @param position
*/
@Override
public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
int headerCount = getHeaderViewCount();
if (itemAdapter.getItemCount() > 0 && position >= headerCount && position < headerCount + itemAdapter.getItemCount()) {
itemAdapter.onBindViewHolder(holder, position - headerCount);
} else {
ViewGroup.LayoutParams layoutParams = holder.itemView.getLayoutParams();
if (layoutParams instanceof StaggeredGridLayoutManager.LayoutParams) {
((StaggeredGridLayoutManager.LayoutParams) layoutParams).setFullSpan(true);
}
}
}
@Override
public int getItemCount() {
if (itemAdapter.getItemCount() == 0) {//没有数据,只显示header+emptyView
return getHeaderViewCount() + getEmptyViewCount();
} else {//有数据不显示emptyView
return getHeaderViewCount() + getFooterViewCount() + itemAdapter.getItemCount();
}
}
private TipsListener tipsListener;
public void setTipsListener(TipsListener tipsListener) {
this.tipsListener = tipsListener;
}
}
各部分监听器就不一一列出了,通过对源码的剖析,可以看出Helper实际上改添了许多关于Tips的状态View
同时也简化了Adapter中的item点击事件监听,使我们可以更好地去处理点击事件,关于RecyclerViewHelper就到这里了.