RecyclerView Adapter 优雅封装,一个Adapter搞定所有列表

项目中,我们用得最多的元素就是列表了,在Android 中,实现列表用原生的RecyclerView就能满足需求,关于RecyclerView 的基础使用这里不做过多的介绍,网上有太多的博文介绍了。本篇文章将介绍自己封装的一个Adapter,帮你快速高效的添加一个列表(包括单 Item 列表和多item列表)。

理念

1, 构造一个通用的Adapter模版,避免每添加一个列表就要写一个Adapter,避免写Adapter中的大量重复代码。
2,通过组装的方式来构建Adapter,将每一种(ViewType不同的)Item抽象成一个单独组件,Adapter 就是一个壳,我们只需要向Adapter中添加Item就行,这样做的好处就是减少耦合,去掉一种item 或者添加一种item对于列表是没有任何影响的。
3,高内聚,低耦合,扩展方便。

思路

为每一种 viewType 定义一个Cell,Cell就是上面提到的独立组件,它负责创建ViewHolder,数据绑定和逻辑处理。它有2个重要的方法,onCreateViewHolder 负责创建ViewHolder,onBindViewHolder负责数据绑定,这两个方法的定义和生命周期同Adapter种的2个方法一样,事实上,Adapter 中的onCreateViewHolder和onBindViewHolder 最终调用的是Cell中的方法。

** 一种 ViewType 对应一个Cell**
看一个示例:

cell_simple.png

如上图:以豆瓣APP的首页为例,文章包含图片和视频的两个Item 的布局是不同的,因此,可以添加两个Cell(ImageCell和VideoCell)来分别处理这两种Item。

** 有了Cell之后,要向列表添加添加Header和Footer 的需求就很简单了,我们直接添加一个HeaderCell和FooterCell 就可以了,也不用更改Adapter代码,是不是很方便。此外,还可以用Cell实现列表LoadMore(加载更多)状态、Loadding(加载中)状态、Empty(空页面)状态、Error(出错)状态 View的显示。**

包结构

rv_pakage.png

介绍:1,base:base包下面为Lib的主要代码,一个Cell接口和三个抽象类,分别抽取了Adapter,ViewHolder,Cell的公共逻辑。
2,cell:cell包下面有4个cell,分别显示列表的LoadMore,Loading,Empty,Error状态。
3,fragment:有一个Fragment抽象类,定义了一个UI模版(不需要额外添加布局文件),要实现列表的界面只需要继承AbsBaseFragment,实现几个方法添加数据就OK。

具体代码

1,Cell 接口定义
/**
 * Created by zhouwei on 17/1/19.
 */

public interface Cell {
    /**
     * 回收资源
     *
     */
    public void releaseResource();

    /**
     * 获取viewType
     * @return
     */
    public  int getItemType();

    /**
     * 创建ViewHolder
     * @param parent
     * @param viewType
     * @return
     */
    public RVBaseViewHolder onCreateViewHolder(ViewGroup parent, int viewType);

    /**
     * 数据绑定
     * @param holder
     * @param position
     */
    public  void onBindViewHolder(RVBaseViewHolder holder, int position);
}

定义了4个方法,除了回收资源的方法releaseResource(),其它三个和Adapter中的一样。

2,RVBaseCell
/**
 * Created by zhouwei on 17/1/19.
 */

public  abstract class RVBaseCell<T> implements Cell {

    public RVBaseCell(T t){
        mData = t;
    }
    public T mData;

    @Override
    public void releaseResource() {
        // do nothing
        // 如果有需要回收的资源,子类自己实现
    }
}

抽象类,接受一个范型T(Cell接受的数据实体),实现了releaseResource方法,但什么事也没干,因为有很多简单的Cell没有资源回收,就不需要实现。如果子类Cell 有资源回收,重写这个方法就可以了。

3, RVBaseViewHolder
/**
 * Created by zhouwei on 17/1/19.
 */

public class RVBaseViewHolder extends RecyclerView.ViewHolder{
    private SparseArray<View> views;
    private View mItemView;
    public RVBaseViewHolder(View itemView) {
        super(itemView);
        views = new SparseArray<>();
        mItemView = itemView;

    }

    /**
     * 获取ItemView
     * @return
     */
    public View getItemView() {
        return mItemView;
    }

    public View getView(int resId) {
        return retrieveView(resId);
    }

    public TextView getTextView(int resId){
        return retrieveView(resId);
    }

    public ImageView getImageView(int resId){
        return retrieveView(resId);
    }

    public Button getButton(int resId){
        return retrieveView(resId);
    }

    @SuppressWarnings("unchecked")
    protected <V extends View> V retrieveView(int viewId){
        View view = views.get(viewId);
        if(view == null){
            view = mItemView.findViewById(viewId);
            views.put(viewId,view);
        }
        return (V) view;
    }

    public void setText(int resId,CharSequence text){
          getTextView(resId).setText(text);
    }

    public void setText(int resId,int strId){
        getTextView(resId).setText(strId);
    }

}

以前写Adapter的时候,每一种viewType 都对应了一个ViewHolder,其中有大量的findViewById绑定视图,有了RVBaseViewHolder,再也不需要定义ViewHolder了,通过id获取View就行,View用SparseArray 保存进行了复用,避免每一次都find。

4,RVBaseAdapter
/**
 * Created by zhouwei on 17/1/19.
 */

public   abstract class RVBaseAdapter<C extends RVBaseCell>  extends RecyclerView.Adapter<RVBaseViewHolder>{
    public static final String TAG = "RVBaseAdapter";
    protected List<C> mData;

    public RVBaseAdapter(){
        mData = new ArrayList<>();
    }

    public void setData(List<C> data) {
        addAll(data);
        notifyDataSetChanged();
    }

    public List<C> getData() {
        return mData;
    }

    @Override
    public RVBaseViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
        for(int i=0;i<getItemCount();i++){
            if(viewType == mData.get(i).getItemType()){
                return mData.get(i).onCreateViewHolder(parent,viewType);
            }
        }

        throw new RuntimeException("wrong viewType");
    }

    @Override
    public void onBindViewHolder(RVBaseViewHolder holder, int position) {
        mData.get(position).onBindViewHolder(holder,position);
    }

    @Override
    public void onViewDetachedFromWindow(RVBaseViewHolder holder) {
        super.onViewDetachedFromWindow(holder);
        Log.e(TAG,"onViewDetachedFromWindow invoke...");
        //释放资源
        int position = holder.getAdapterPosition();
        //越界检查
        if(position<0 || position>=mData.size()){
            return;
        }
        mData.get(position).releaseResource();
    }


    @Override
    public int getItemCount() {
        return mData == null ? 0:mData.size();
    }

    @Override
    public int getItemViewType(int position) {
        return mData.get(position).getItemType();
    }

    /**
     * add one cell
     * @param cell
     */
    public void add(C cell){
         mData.add(cell);
         int index = mData.indexOf(cell);
         notifyItemChanged(index);
    }

    public void add(int index,C cell){
        mData.add(index,cell);
        notifyItemChanged(index);
    }

    /**
     * remove a cell
     * @param cell
     */
    public void remove(C cell){
        int indexOfCell = mData.indexOf(cell);
        remove(indexOfCell);
    }

    public void remove(int index){
        mData.remove(index);
        notifyItemRemoved(index);
    }

    /**
     *
     * @param start
     * @param count
     */
    public void remove(int start,int count){
        if((start +count) > mData.size()){
            return;
        }
        int size = getItemCount();
        for(int i =start;i<size;i++){
            mData.remove(i);
        }

        notifyItemRangeRemoved(start,count);
    }




    /**
     * add a cell list
     * @param cells
     */
    public void addAll(List<C> cells){
        if(cells == null || cells.size() == 0){
            return;
        }
        Log.e("zhouwei","addAll cell size:"+cells.size());
        mData.addAll(cells);
        notifyItemRangeChanged(mData.size()-cells.size(),mData.size());
    }

    public void addAll(int index,List<C> cells){
        if(cells == null || cells.size() == 0){
            return;
        }
        mData.addAll(index,cells);
        notifyItemRangeChanged(index,index+cells.size());
    }

    public void clear(){
        mData.clear();
        notifyDataSetChanged();
    }


    /**
     * 如果子类需要在onBindViewHolder 回调的时候做的操作可以在这个方法里做
     * @param holder
     * @param position
     */
    protected abstract void onViewHolderBound(RVBaseViewHolder holder, int position);

}

RVBaseAdapter 继承 RecyclerView.Adapter,接受的是RVBaseCell类型,保存了一个Cell 列表。其中还有有添加、移除,清空、更新数据的方法。
注意其中几个方法:
1,getItemViewType: 调用的是对应position Cell 的getItemViewType 方法。

2,onCreateViewHolder:调用Cell 的onCreateViewHolder 创建ViewHolder。

3,onBindViewHolder: 调用对应Cell的onBindViewHolder 方法绑定数据

4,onViewDetachedFromWindow: 资源回收

5,RVSimpleAdapter
/**
 * Created by zhouwei on 17/1/23.
 */

public class RVSimpleAdapter extends RVBaseAdapter{
    public static final int ERROR_TYPE = Integer.MAX_VALUE -1;
    public static final int EMPTY_TYPE = Integer.MAX_VALUE -2;
    public static final int LOADING_TYPE = Integer.MAX_VALUE -3;
    public static final int LOAD_MORE_TYPE = Integer.MAX_VALUE -4;

    private EmptyCell mEmptyCell;
    private ErrorCell mErrorCell;
    private LoadingCell mLoadingCell;
    private LoadMoreCell mLoadMoreCell;
    //LoadMore 是否已显示
    private boolean mIsShowLoadMore = false;

    public RVSimpleAdapter(){
        mEmptyCell = new EmptyCell(null);
        mErrorCell = new ErrorCell(null);
        mLoadingCell = new LoadingCell(null);
        mLoadMoreCell = new LoadMoreCell(null);
    }
    @Override
    protected void onViewHolderBound(RVBaseViewHolder holder, int position) {

    }

    @Override
    public void onAttachedToRecyclerView(RecyclerView recyclerView) {
        super.onAttachedToRecyclerView(recyclerView);
        RecyclerView.LayoutManager manager = recyclerView.getLayoutManager();
        //处理GridView 布局
        if(manager instanceof GridLayoutManager){
            final GridLayoutManager gridLayoutManager = (GridLayoutManager) manager;
            gridLayoutManager.setSpanSizeLookup(new GridLayoutManager.SpanSizeLookup() {
                @Override
                public int getSpanSize(int position) {
                    int viewType = getItemViewType(position);
                    return (viewType == ERROR_TYPE|| viewType == EMPTY_TYPE || viewType == LOADING_TYPE
                    ||viewType == LOAD_MORE_TYPE) ? gridLayoutManager.getSpanCount():1;
                }
            });
        }

    }

    @Override
    public void onViewAttachedToWindow(RecyclerView.ViewHolder holder) {
        // 处理StaggeredGridLayoutManager 显示这个Span
        int position = holder.getAdapterPosition();
        int viewType = getItemViewType(position);
        if(isStaggeredGridLayout(holder)){
            if(viewType == ERROR_TYPE|| viewType == EMPTY_TYPE || viewType == LOADING_TYPE
                    ||viewType == LOAD_MORE_TYPE){

                StaggeredGridLayoutManager.LayoutParams params = (StaggeredGridLayoutManager.LayoutParams) holder.itemView.getLayoutParams();
                //设置显示整个span
                params.setFullSpan(true);
            }
        }

    }

    private boolean isStaggeredGridLayout(RecyclerView.ViewHolder holder) {
        ViewGroup.LayoutParams layoutParams = holder.itemView.getLayoutParams();
        if (layoutParams != null && layoutParams instanceof StaggeredGridLayoutManager.LayoutParams) {
            return true;
        }
        return false;
    }

    /**
     * 显示LoadingView
     * <p>请求数据时调用,数据请求完毕时调用{@link #hideLoading }</p>
     * @see #showLoadingKeepCount(int)
     */
    public void showLoading(){
      clear();
      add(mLoadingCell);
    }

    public void showLoading(View loadingView){
        if(loadingView == null){
            showLoading();
        }
        clear();
        mLoadingCell.setLoadingView(loadingView);
        add(mLoadingCell);
    }
    /**
     * 显示LoadingView
     * <p>列表显示LoadingView并保留keepCount个Item</p>
     * @param keepCount 保留的条目数量
     */
    public void showLoadingKeepCount(int keepCount){
        if(keepCount < 0 || keepCount>mData.size()){
            return;
        }
        remove(keepCount,mData.size() - keepCount);
        if(mData.contains(mLoadingCell)){
            mData.remove(mLoadingCell);
        }
        add(mLoadingCell);
    }
    /**
     * hide Loading view
     */
    public void hideLoading(){
        if(mData.contains(mLoadingCell)){
            mData.remove(mLoadingCell);
        }
    }

    /**
     * 显示错误提示
     * <p>当网络请求发生错误,需要在界面给出错误提示时,调用{@link #showError}</p>
     * @see #showErrorKeepCount(int)
     */
    public void showError(){
       clear();
       add(mErrorCell);
    }

    /**
     * 显示错误提示
     * <p>当网络请求发生错误,需要在界面给出错误提示时,调用{@link #showErrorKeepCount(int)},并保留keepCount 条Item</p>
     * @param keepCount 保留Item数量
     */
    public void showErrorKeepCount(int keepCount){
        if(keepCount < 0 || keepCount>mData.size()){
            return;
        }
        remove(keepCount,mData.size() - keepCount);
        if(mData.contains(mErrorCell)){
            mData.remove(mErrorCell);
        }
        add(mErrorCell);

    }

    /**
     * 隐藏错误提示
     */
    public void hideErorr(){
       if(mData.contains(mErrorCell)){
           remove(mErrorCell);
       }
    }

    /**
     * 显示LoadMoreView
     * <p>当列表滑动到底部时,调用{@link #showLoadMore()} 提示加载更多,加载完数据,调用{@link #hideLoadMore()}
     * 隐藏LoadMoreView,显示列表数据。</p>
     *
     */
    public void showLoadMore(){
       if(mData.contains(mLoadMoreCell)){
           return;
       }
       add(mLoadMoreCell);
       mIsShowLoadMore = true;
    }

    /**
     * 隐藏LoadMoreView
     * <p>调用{@link #showLoadMore()}之后,加载数据完成,调用{@link #hideLoadMore()}隐藏LoadMoreView</p>
     */
    public void hideLoadMore(){
       if(mData.contains(mLoadMoreCell)){
           remove(mLoadMoreCell);
           mIsShowLoadMore = false;
       }
    }

    /**
     * LoadMore View 是否已经显示
     * @return
     */
    public boolean isShowLoadMore() {
        return mIsShowLoadMore;
    }

    /**
     *
     * @param keepCount
     */
    public void showEmptyKeepCount(int keepCount){
        if(keepCount < 0 || keepCount>mData.size()){
            return;
        }
        remove(keepCount,mData.size() - keepCount);
        if(mData.contains(mEmptyCell)){
            mData.remove(mEmptyCell);
        }
        add(mEmptyCell);

    }

    /**
     * 显示空view
     * <p>当页面没有数据的时候,调用{@link #showEmpty()}显示空View,给用户提示</p>
     */
    public void showEmpty(){
      clear();
      add(mEmptyCell);
    }

    /**
     * 隐藏空View
     */
    public void hideEmpty(){
      if(mData.contains(mEmptyCell)){
          remove(mEmptyCell);
      }
    }
}

RVSimpleAdapter 是RVBaseAdapter 的默认实现类,添加了显示LoadMore View、Loading View 、Empty View、ErrorView 的功能。

6,AbsBaseFragment
/**
 * Created by zhouwei on 17/2/3.
 */

public abstract class AbsBaseFragment<T> extends Fragment {
    protected RecyclerView mRecyclerView;
    protected RVSimpleAdapter mBaseAdapter;
    private FrameLayout mToolbarContainer;
    protected SwipeRefreshLayout mSwipeRefreshLayout;
    /**
     * RecyclerView 最后可见Item在Adapter中的位置
     */
    private int mLastVisiblePosition = -1;

    @Nullable
    @Override
    public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
        View view =  inflater.inflate(R.layout.base_fragment_layout,null);
        return view;
    }

    @Override
    public void onViewCreated(View view, @Nullable Bundle savedInstanceState) {
        super.onViewCreated(view, savedInstanceState);
        mSwipeRefreshLayout = (SwipeRefreshLayout) view.findViewById(R.id.base_refresh_layout);
        mToolbarContainer = (FrameLayout) view.findViewById(R.id.toolbar_container);
        mRecyclerView = (RecyclerView) view.findViewById(R.id.base_fragment_rv);
        mRecyclerView.setLayoutManager(initLayoutManger());
        mBaseAdapter = initAdapter();
        mRecyclerView.setAdapter(mBaseAdapter);
        mSwipeRefreshLayout.setOnRefreshListener(new SwipeRefreshLayout.OnRefreshListener() {
            @Override
            public void onRefresh() {
                setRefreshing(true);
                onPullRefresh();
            }
        });
        mRecyclerView.addOnScrollListener(new RecyclerView.OnScrollListener() {
            @Override
            public void onScrolled(RecyclerView recyclerView, int dx, int dy) {

                RecyclerView.LayoutManager layoutManager = recyclerView.getLayoutManager();
                if(layoutManager instanceof LinearLayoutManager){
                    mLastVisiblePosition = ((LinearLayoutManager)layoutManager).findLastVisibleItemPosition();
                }else if(layoutManager instanceof GridLayoutManager){
                    mLastVisiblePosition = ((GridLayoutManager)layoutManager).findLastVisibleItemPosition();
                }else if(layoutManager instanceof StaggeredGridLayoutManager){
                    StaggeredGridLayoutManager staggeredGridLayoutManager = (StaggeredGridLayoutManager) layoutManager;
                    int []lastPositions = new int[staggeredGridLayoutManager.getSpanCount()];
                    staggeredGridLayoutManager.findLastVisibleItemPositions(lastPositions);
                    mLastVisiblePosition = findMax(lastPositions);
                }
            }

            @Override
            public void onScrollStateChanged(RecyclerView recyclerView, int newState) {
                View firstView = recyclerView.getChildAt(0);
                int top = firstView.getTop();
                int topEdge = recyclerView.getPaddingTop();
                //判断RecyclerView 的ItemView是否满屏,如果不满一屏,上拉不会触发加载更多
                boolean isFullScreen = top < topEdge;

                RecyclerView.LayoutManager manager = recyclerView.getLayoutManager();
                int itemCount = manager.getItemCount();
                //因为LoadMore View  是Adapter的一个Item,显示LoadMore 的时候,Item数量+1了,导致 mLastVisibalePosition == itemCount-1
                // 判断两次都成立,因此必须加一个判断条件 !mBaseAdapter.isShowLoadMore()
                if(newState == RecyclerView.SCROLL_STATE_IDLE && mLastVisiblePosition == itemCount-1 && isFullScreen && !mBaseAdapter.isShowLoadMore()){
                   //最后一个Item了
                   mBaseAdapter.showLoadMore();
                   onLoadMore();
                }
            }
        });
        View toolbarView = addToolbar();
        if(toolbarView!=null && mToolbarContainer!=null
                ){
            mToolbarContainer.addView(toolbarView);
        }
        onRecyclerViewInitialized();

    }

    /**
     * hide load more progress
     */
    public void hideLoadMore(){
        if(mBaseAdapter!=null){
            mBaseAdapter.hideLoadMore();
        }
    }

    /**
     * 获取组数最大值
     * @param lastPositions
     * @return
     */
    private int findMax(int[] lastPositions) {
        int max = lastPositions[0];
        for (int value : lastPositions) {
            if (value > max) {
                max = value;
            }
        }
        return max;
    }

    /**
     * 设置刷新进度条的颜色
     * see{@link SwipeRefreshLayout#setColorSchemeResources(int...)}
     * @param colorResIds
     */
    public void setColorSchemeResources(@ColorRes int... colorResIds){
        if(mSwipeRefreshLayout!=null){
            mSwipeRefreshLayout.setColorSchemeResources(colorResIds);
        }
    }

    /**
     * 设置刷新进度条的颜色
     * see{@link SwipeRefreshLayout#setColorSchemeColors(int...)}
     * @param colors
     */
    public void setColorSchemeColors(int... colors){
        if(mSwipeRefreshLayout!=null){
            mSwipeRefreshLayout.setColorSchemeColors(colors);
        }
    }

    /**
     * 设置刷新进度条背景色
     *  see{@link SwipeRefreshLayout#setProgressBackgroundColorSchemeResource(int)} (int)}
     * @param colorRes
     */
    public void setProgressBackgroundColorSchemeResource(@ColorRes int colorRes) {
         if(mSwipeRefreshLayout!=null){
             mSwipeRefreshLayout.setProgressBackgroundColorSchemeResource(colorRes);
         }
    }

    /**
     * 设置刷新进度条背景色
     *  see{@link SwipeRefreshLayout#setProgressBackgroundColorSchemeColor(int)}
     * @param color
     */
    public void setProgressBackgroundColorSchemeColor(@ColorInt int color) {
       if(mSwipeRefreshLayout!=null){
           mSwipeRefreshLayout.setProgressBackgroundColorSchemeColor(color);
       }
    }

    /**
     * Notify the widget that refresh state has changed. Do not call this when
     * refresh is triggered by a swipe gesture.
     *
     * @param refreshing Whether or not the view should show refresh progress.
     */
    public void setRefreshing(boolean refreshing){
        if(mSwipeRefreshLayout== null){
            return;
        }
        mSwipeRefreshLayout.setRefreshing(refreshing);
    }

    /**
     * 子类可以自己指定Adapter,如果不指定默认RVSimpleAdapter
     * @return
     */
    protected RVSimpleAdapter initAdapter(){
        return new RVSimpleAdapter();
    }

    /**
     * 子类自己指定RecyclerView的LayoutManager,如果不指定,默认为LinearLayoutManager,VERTICAL 方向
     * @return
     */
    protected RecyclerView.LayoutManager initLayoutManger(){
        LinearLayoutManager manager = new LinearLayoutManager(getContext());
        manager.setOrientation(LinearLayoutManager.VERTICAL);
        return manager;
    }

    /**
     * 添加TitleBar
     * @param
     */
    public View addToolbar(){
      //如果需要Toolbar,子类返回Toolbar View
      return null;
    }

    /**
     *RecyclerView 初始化完毕,可以在这个方法里绑定数据
     */
    public abstract void onRecyclerViewInitialized();

    /**
     * 下拉刷新
     */
    public abstract void onPullRefresh();

    /**
     * 上拉加载更多
     */
    public abstract void onLoadMore();

    /**
     *  根据实体生成对应的Cell
     * @param list  实体列表
     * @return cell列表
     */
    protected abstract  List<Cell> getCells(List<T> list);

}

AbsBaseFragment,实现了上拉加载和下拉刷新功能,添加Toolbar等,上拉加载可以自定义View,下拉刷新用的是Google的SwipeRefreshLayout。要添加一个列表界面,只需要继承AbsBaseFragment,实现几个抽象方法添加Cell就行了,非常方便。

用法示例:

添加一个多Item的列表:

1,创建一个Fragment继承AbsBaseFragment,实现几个方法。

/**
 * Created by zhouwei on 17/2/3.
 */

public class HomePageFragment extends AbsBaseFragment<Entry> {
    @Override
    public void onRecyclerViewInitialized() {
       //初始化View和数据加载 
    }

    @Override
    public void onPullRefresh() {
        //下拉刷新回调
       
    }

    @Override
    public void onLoadMore() {
        //上拉加载回调
    }
    protected List<Cell> getCells(List<Entry> entries){
        //根据实体生成Cell
        return null;
    }

}

实现上面几个抽象方法,实际上只实现onRecyclerViewInitialized和getCells两个方法就可以实现列表,其它两个方法是下拉刷新和上拉加载的。

2,创建Cell类

/**
 * Created by zhouwei on 17/2/7.
 */

public class BannerCell extends RVBaseCell<List<String>> {
    public static final int TYPE = 2;
    private ConvenientBanner mConvenientBanner;
    public BannerCell(List<String> strings) {
        super(strings);
    }

    @Override
    public int getItemType() {
        return TYPE;
    }

    @Override
    public RVBaseViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
        return new RVBaseViewHolder(LayoutInflater.from(parent.getContext()).inflate(R.layout.grid_cell_layoout,null));
    }

    @Override
    public void onBindViewHolder(RVBaseViewHolder holder, int position) {
       mConvenientBanner = (ConvenientBanner) holder.getView(R.id.banner);
        mConvenientBanner.setPages(new CBViewHolderCreator<NetworkImageHolderView>() {
            @Override
            public NetworkImageHolderView createHolder() {
                return new NetworkImageHolderView();
            }
        }, mData);
        mConvenientBanner.startTurning(2000);
    }

    @Override
    public void releaseResource() {
        if(mConvenientBanner!=null){
            mConvenientBanner.stopTurning();
        }
    }

    public static class NetworkImageHolderView implements CBPageAdapter.Holder<String>{
        private ImageView imageView;
        @Override
        public View createView(Context context) {
            imageView = new ImageView(context);
            imageView.setScaleType(ImageView.ScaleType.CENTER_CROP);
            return imageView;
        }

        @Override
        public void UpdateUI(Context context, int position, String data) {
            ImageLoader.getInstance().displayImage(data,imageView);
        }
    }
}
/**
 * Created by zhouwei on 17/1/19.
 */

public class ImageCell extends RVBaseCell<Entry> {
    public static final int TYPE = 1;
    public ImageCell(Entry entry) {
        super(entry);
    }

    @Override
    public int getItemType() {
        return TYPE;
    }

    @Override
    public RVBaseViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
        return new RVBaseViewHolder(LayoutInflater.from(parent.getContext()).inflate(R.layout.image_cell_layout,null));
    }

    @Override
    public void onBindViewHolder(RVBaseViewHolder holder, int position) {
        Picasso.with(holder.getItemView().getContext()).load(mData.imageUrl).into(holder.getImageView(R.id.image));
    }

}
/**
 * Created by zhouwei on 17/1/19.
 */

public class TextCell extends RVBaseCell<Entry> {
    public static final int TYPE = 0;
    public TextCell(Entry entry) {
        super(entry);
    }

    @Override
    public int getItemType() {
        return TYPE;
    }

    @Override
    public RVBaseViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
        return new RVBaseViewHolder(LayoutInflater.from(parent.getContext()).inflate(R.layout.text_cell_layout,null));
    }

    @Override
    public void onBindViewHolder(RVBaseViewHolder holder, int position) {
       holder.setText(R.id.text_content,mData.content);
    }
}

上面创建了3个Cell,也就是这个列表包含了3种不同类型的Item。

注意:一个列表内,每个Cell 的TYPE要不相同,也就是getItemType方法的返回值要不同。

3,onRecyclerViewInitialized ,做初始化和加载数据

@Override
    public void onRecyclerViewInitialized() {
       //初始化View和数据加载
       //设置刷新进度条颜色
       setColorSchemeResources(R.color.colorAccent);
       loadData();
    }

    /**
     * 模拟从服务器取数据
     */
    private void loadData(){
        View loadingView = LayoutInflater.from(getContext()).inflate(R.layout.manu_loading_layout,null);
        mBaseAdapter.showLoading(loadingView);
        mRecyclerView.postDelayed(new Runnable() {
            @Override
            public void run() {
                mBaseAdapter.hideLoading();
                mBaseAdapter.addAll(getCells(mockData()));
            }
        },2000);
    }

4,实现getCells方法,生成Cell


   protected List<Cell> getCells(List<Entry> entries){
        //根据实体生成Cell
        List<Cell> cells = new ArrayList<>();
        cells.add(new BannerCell(Arrays.asList(DataMocker.images)));
        for (int i=0;i<entries.size();i++){
            Entry entry = entries.get(i);
            if(entry.type == Entry.TYPE_IMAGE){
                cells.add(new ImageCell(entry));
            }else{
                cells.add(new TextCell(entry));
            }
        }
        return cells;
    }

上面根据实体生成不同的Cell。有三种Cell,BannerCell,ImageCell和TextCell。

以上4个步骤就能实现一个界面复杂包含多做Item的列表了效果图如下:

adapter_cell.gif

HomePageFragment 的完整代码如下:

/**
 * Created by zhouwei on 17/2/3.
 */

public class HomePageFragment extends AbsBaseFragment<Entry> {
    @Override
    public void onRecyclerViewInitialized() {
       //初始化View和数据加载
       //设置刷新进度条颜色
       setColorSchemeResources(R.color.colorAccent);
       loadData();
    }

    @Override
    public void onPullRefresh() {
        //下拉刷新回调
        mRecyclerView.postDelayed(new Runnable() {
            @Override
            public void run() {
               setRefreshing(false);
            }
        },2000);
    }

    @Override
    public void onLoadMore() {
        //上拉加载回调
       loadMore();
    }


    private void loadMore(){
        mRecyclerView.postDelayed(new Runnable() {
            @Override
            public void run() {
                hideLoadMore();
                mBaseAdapter.addAll(getCells(mockMoreData()));
            }
        },10000);
    }

    protected List<Cell> getCells(List<Entry> entries){
        //根据实体生成Cell
        List<Cell> cells = new ArrayList<>();
        cells.add(new BannerCell(Arrays.asList(DataMocker.images)));
        for (int i=0;i<entries.size();i++){
            Entry entry = entries.get(i);
            if(entry.type == Entry.TYPE_IMAGE){
                cells.add(new ImageCell(entry));
            }else{
                cells.add(new TextCell(entry));
            }
        }
        return cells;
    }


    @Override
    public View addToolbar() {
        View toolbar = LayoutInflater.from(getContext()).inflate(R.layout.title_bar_layout,null);
        return toolbar;
    }

    /**
     * 模拟从服务器取数据
     */
    private void loadData(){
        View loadingView = LayoutInflater.from(getContext()).inflate(R.layout.manu_loading_layout,null);
        mBaseAdapter.showLoading(loadingView);
        mRecyclerView.postDelayed(new Runnable() {
            @Override
            public void run() {
                mBaseAdapter.hideLoading();
                mBaseAdapter.addAll(getCells(mockData()));
            }
        },2000);
    }
}
Grid 列表和瀑布流列表:

上面演示了添加多Item type 的列表,添加单Item的列表也是一样的,只不过只有一个Cell而已。添加Grid 列表和瀑布流列表差不多的,只是RecylerView 的LayoutManager不同而已。

瀑布流列表示例:

/**
 * Created by zhouwei on 17/2/4.
 */

public class DetailFragment extends AbsBaseFragment<DetailEntry> {
    @Override
    public void onRecyclerViewInitialized() {
         mBaseAdapter.setData(getCells(mockStaggerData()));
    }

    @Override
    public void onPullRefresh() {

    }

    @Override
    public void onLoadMore() {

    }

    @Override
    protected RecyclerView.LayoutManager initLayoutManger() {
        StaggeredGridLayoutManager layoutManager = new StaggeredGridLayoutManager(2,StaggeredGridLayoutManager.VERTICAL);
        return layoutManager;
    }

    @Override
    protected List<Cell> getCells(List<DetailEntry> list) {
        List<Cell> cells = new ArrayList<>();
        for (int i=0;i<list.size();i++){
            cells.add(new DetailCell(list.get(i)));
        }
        return cells;
    }
    
}

只需要重写initLayoutManager这个方法,返回一个瀑布流的LayoutMannger就可以了。
效果如下:

stagger_adapter_cell.gif

其它演示示例:LoadMore View 、Loading View 、Error View ,Empty View

1,显示LoadMore View
提供了默认的Loading View,调用代码如下:

 mBaseAdapter.showLoadMore();

如果不想用默认的LoadMore View,当然也可以自定义LoadMore View,Adapter 提供方法:

mBaseAdapter.showLoadMore(loadMoreView);

像上面这样提供一个LoadMore View 的布局,还有一个重载的方法,可以指定显示的高度:

   mBaseAdapter.showLoadMore(loadMoreView,100);

如果是继承的AbsBaseFragment 创建列表,实现customLoadMoreView方法就ok了:

 @Override
    protected View customLoadMoreView() {
        View loadMoreView = LayoutInflater.from(getContext()).inflate(R.layout.custeom_load_more_layout,null);
        return loadMoreView;
    }

隐藏LoadMore View 调用如下代码:

 if(mBaseAdapter!=null){
            mBaseAdapter.hideLoadMore();
   }

效果图看上面演示的瀑布流效果图。

**2,显示loading View **
提供了默认的Loading View,调用代码如下:

mBaseAdapter.showLoading();

当然也可以自定义Loading View,提供一个布局即可:

View loadingView = LayoutInflater.from(getContext()).inflate(R.layout.manu_loading_layout,null);
 mBaseAdapter.showLoading(loadingView);

效果如下:

loading_view.png

还有一种情况是,顶部有一个固定的HeaderCell,不需要加载数据,显示静态页面,下面加载数据时需要Loading态,Error状态,Empty状态等等。提供如下3个方法:

  • showLoadingKeepCount(int keepCount,int height,View loadingView)
    列表Loading状态显示的View,保留keepCountg个Item,并指定高度,指定显示的View

  • showLoadingKeepCount(int keepCount,int height)
    列表Loading状态显示的View,保留keepCountg个Item,并指定高度(显示的是提供的默认Loading View)

  • showLoadingKeepCount(int keepCount)
    显示默认LoadingView

使用代码如下:

 View loadingView = LayoutInflater.from(getContext()).inflate(R.layout.manu_loading_layout,null);
mBaseAdapter.showLoadingKeepCount(1,height,loadingView);

效果图如下:

loading_view_keep_count.png

隐藏Loading View 调用对应hide 方法:

 mBaseAdapter.hideLoading();

** 3, Error View 和 Empty View **
显示Error View 和Empty View 与Loading View 的显示与隐藏是一样,不在过多讲,直接去看源码,提供了几个方法:

error_method.png

效果图:

error_tip.png

Empty View 的显示完全一样,就不再讲了。

最后

以上就是对RecyclerView Adapter 的封装和 该库的使用介绍,使用起来非常方便,添加一个列表不再是重复的写Adapter,ViewHolder 等等。添加一个Cell 填充到Adapter 就OK。增加一种Item或者加少一种Item对列表完全没有影响,耦合度几乎为0。更详细的思路分析请看文章RecyclerView 之Adapter的简化过程浅析详细的源码请看Gihub:
Adapter优雅封装-CustomAdapter,欢迎star和follow。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 159,015评论 4 362
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,262评论 1 292
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 108,727评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,986评论 0 205
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,363评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,610评论 1 219
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,871评论 2 312
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,582评论 0 198
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,297评论 1 242
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,551评论 2 246
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,053评论 1 260
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,385评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,035评论 3 236
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,079评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,841评论 0 195
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,648评论 2 274
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,550评论 2 270

推荐阅读更多精彩内容