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就到这里了.