RecyclerView Adapter elegant package to get all lists

Reprinted from: Still Fantasy

RecycleView loads the list, encapsulates the Adapter, and adds a list quickly and efficiently (including single item lists and multi-item lists).

idea

1, Construct a general Adapter template, avoid writing an Adapter every time you add a list, and avoid writing a lot of repetitive code in the Adapter.
2. Adapter is constructed by assembly method, and each item (with different ViewType) is abstracted into a separate component. Adapter is a shell. We only need to add Item to Adapter. The advantage of this is to reduce coupling and remove An item or adding an item has no effect on the list.
3. High cohesion, low coupling and easy expansion.
Ideas

Define a Cell for each viewType. Cell is the independent component mentioned above, which is responsible for creating ViewHolder, data binding and logic processing. It has two important methods, onCreateViewHolder is responsible for creating ViewHolder, and onBindViewHolder is responsible for data binding. The definition and life cycle of these two methods are the same as the two methods of Adapter. In fact, onCreateViewHolder and onBindViewHolder in Adapter are finally called Method in Cell.


See an example for a ViewType corresponding to a Cell :

cell_simple.png

As shown in the figure above: Take the homepage of Douban APP as an example. The layout of the two items containing pictures and videos in the article is different. Therefore, two Cells (ImageCell and VideoCell) can be added to handle these two items separately.

After having Cell, it is very simple to add Header and Footer to the list. We can add a HeaderCell and FooterCell directly, without changing the Adapter code, is it very convenient. In addition, you can also use Cell to display the list LoadMore status, Loadding status, Empty (empty page) status, and Error status View display.
Package structure

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。

Specific code
1, Cell interface definition

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);
}

Four methods are defined, except for the method releaseResource(), which reclaims resources, the other three are the same as those in Adapter.
2,RVBaseCpublic abstract class RVBaseCell implements Cell {

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

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

}, accept a paradigm T (the data entity accepted by the Cell) and implement the releaseResource method, but nothing is done, because there are many simple Cells that do not have resource recovery and do not need to be implemented. If the subclass Cell has resource recovery, just rewrite this method.
3, RVBaseViewHol public class RVBaseViewHolder extends RecyclerView.ViewHolder{
private SparseArray 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);
}

} In the case of Adapter, each viewType corresponds to a ViewHolder, among which there are a large number of findViewById binding views. With RVBaseViewHolder, there is no need to define ViewHolder anymore, just get the View by id, and the View is saved with SparseArray for reuse , To avoid finding every time.
4. RVBaseAdappublic abstract class RVBaseAdapter extends RecyclerView.Adapter{
public static final String TAG = "RVBaseAdapter";
protected List 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);

}aseAdapter inherits RecyclerView.Adapter, accepts RVBaseCell type, and saves a Cell list. There are also methods for adding, removing, clearing, and updating data.
Pay attention to several methods:
1. getItemViewType: The getItemViewType method corresponding to the position Cell is called.

2. onCreateViewHolder: call onCreateViewHolder of Cell to create ViewHolder.

3. onBindViewHolder: call the onBindViewHolder method of the corresponding Cell to bind data

4,onViewDetachedFromWindow: 资源回收
5,RVSimpleAdaptpublic 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);
  }
}

}impleAdapter is the default implementation class of RVBaseAdapter, adding the functions of displaying LoadMore View, Loading View, Empty View and ErrorView.
6, AbsBaseFragmpublic abstract class AbsBaseFragment extends Fragment {
protected RecyclerView mRecyclerView;
protected RVSimpleAdapter mBaseAdapter;
private FrameLayout mToolbarContainer;
protected SwipeRefreshLayout mSwipeRefreshLayout;
/**
* RecyclerView finally shows the position of Item in Adapter
*/
private int mLast -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);

}BaseFragment, implements the pull-up loading and pull-down refresh functions, add Toolbar, etc., pull-up loading can customize the View, pull-down refresh uses Google's SwipeRefreshLayout. To add a list interface, you only need to inherit AbsBaseFragment and implement several abstract methods to add Cell, which is very convenient.
Usage example:
add a multi-Item list:

1. Create a Fragment to inherit from AbsBaseFragment and implement several methods.

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;
    }

}

To implement the above abstract methods, in fact, only implement the onRecyclerViewInitialized and getCells methods to implement the list, and the other two methods are pull-down refresh and pull-up loading.

2, Create the Cell class

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);
        }
    }
}
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));
    }

}


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 Cells are created above, that is, this list contains 3 different types of Item.

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

3. onRecyclerViewInitialized, do initialization and load data

@Override
public void onRecyclerViewInitialized() {
//Initialize View and data loading
//Set the refresh progress bar color
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. Implement the getCells method to generate 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;
    }

The above generates different Cells according to the entity. There are three kinds of Cell, BannerCell, ImageCell and TextCell.

The above 4 steps can achieve a complex interface with a list of multiple items. The renderings are as follows:

Write picture description here

The complete code of HomePageFragment is as follows:

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 list and waterfall list:

The above demonstrates adding a list of multiple Item types, and adding a single Item list is also the same, except that there is only one Cell. Adding the Grid list is similar to the waterfall list, except that the LayoutManager of RecylerView is different.

Example of waterfall list:

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;
    }

}

Just rewrite the initLayoutManager method and return a waterfall LayoutMannger.
The effect is as follows:
Write picture description here

Other demonstration examples: LoadMore View, Loading View, Error View, Empty View

1. Display LoadMore View
provides the default Loading View, the calling code is as follows:

mBaseAdapter.showLoadMore();

If you don’t want to use the default LoadMore View, you can also customize LoadMore View. Adapter provides methods:

mBaseAdapter.showLoadMore(loadMoreView);

Provide a LoadMore View layout like the above, and an overloaded method to specify the height of the display:

mBaseAdapter.showLoadMore(loadMoreView,100);

If it is an inherited AbsBaseFragment to create a list, it is ok to implement the customLoadMoreView method:

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

To hide LoadMore View, call the following code:

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

For the effect picture, see the waterfall effect picture shown above.

2, Display loading View
provides the default Loading View, the calling code is as follows:

mBaseAdapter.showLoading();

Of course, you can also customize the Loading View and provide a layout:

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

The effect is as follows:

loading_view.png

Another situation is that there is a fixed HeaderCell at the top, which does not need to load data and displays a static page. When loading data below, the Loading state, Error state, Empty state and so on are required. Provide the following 3 methods:

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

The code used is as follows:

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

The renderings are as follows:
Write picture description here

loading_view_keep_count.png

Hide Loading View and call the corresponding hide method:

mBaseAdapter.hideLoading();

3, Error View and Empty View
display Error View, Empty View and Loading View display and hide are the same, not to go too much, just look at the source code, and provide several methods:
error_method.png

Effect picture:Write picture description here

The display of Empty View is exactly the same, so I won't talk about it again.
At last

The above is the introduction to the package of RecyclerView Adapter and the use of the library. It is very convenient to use. Adding a list is no longer repeated writing Adapter, ViewHolder, etc. Add a Cell to fill in the Adapter and it will be OK. Adding one item or adding one less item has no effect on the list, and the coupling degree is almost 0. For a more detailed analysis of the ideas, please see the article RecyclerView's Simplified Process Analysis of Adapter. For detailed source code, please see Gihub: Adapter Elegant Package-CustomAdapter

Guess you like

Origin blog.csdn.net/hai_jian/article/details/73800970