Skip to content

RV Adapter 优雅封装,抽取列表模版,可以快速的添加一个列表,使用组装的方式构建Adapter,抽象Cell 角色,负责创建ViewHolder,绑定数据和逻辑处理。Cell为一个独立的组件。

pinguo-zhouwei/CustomAdapter

Repository files navigation

Release

RecyclerView Adapter 优雅封装,一个Adapter搞定所有列表,快速构建一个列表

目的

1, 构造一个通用的Adapter模版,避免每添加一个列表就要写一个Adapter,避免写Adapter中的大量重复代码。

2,通过组装的方式来构建Adapter,将每一种(ViewType不同的)Item抽象成一个单独组件,Adapter 就是一个壳,我们只需要向Adapter中添加Item就行,这样做的好处就是减少耦合,去掉一种item 或者添加一种item对于列表是没有任何影响的。

3,高内聚,低耦合,扩展方便。

添加依赖

1 Add it in your root build.gradle at the end of repositories:

allprojects {
		repositories {
			...
			maven { url 'https://jitpack.io' }
		}
	}

2 Add the dependency

dependencies {
	 compile 'com.github.pinguo-zhouwei:CustomAdapter:v1.0.3'
}

Change Log

  • v1.0.2 : 修复了调用Adapter 的remove(int,int)方法会crush

  • v1.0.3 : 修复使用AbsBaseFragment 无数据时,下拉刷新crush的bug

思路

为每一种 viewType 定义一个Cell,Cell就是上面提到的独立组件,它负责创建ViewHolder,数据绑定和逻辑处理。 它有2个重要的方法,onCreateViewHolder 负责创建ViewHolder,onBindViewHolder负责数据绑定,这两个方法的定义和生命周期同Adapter种的2个方法一样, 事实上,Adapter 中的onCreateViewHolder和onBindViewHolder 最终调用的是Cell中的方法。, 就像是Adapter上面有很多插槽,我们将一个个Cell插到Adapter上,即插即用。完全解耦,以后添加需求和砍掉需求,只需要增加一种Cell 或者减少一种Cell就行。

new_adapter

详细介绍

详细介绍请看博客:

1,RecyclerView 之Adapter的简化过程浅析

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

特性

1,快速构建一个列表(多item或者单item)

2,带有下拉刷新(Google 的SwipeRefreshLayout)

3,上拉加载更多(可自定义)

4,显示列表Loading态(可自定义)

5,显示列表出错状态(可自定义)

6,显示列表空状态(可自定义)

如何快速构建一个列表?

添加一个多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

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 的显示完全一样,详情请看源码。

About

RV Adapter 优雅封装,抽取列表模版,可以快速的添加一个列表,使用组装的方式构建Adapter,抽象Cell 角色,负责创建ViewHolder,绑定数据和逻辑处理。Cell为一个独立的组件。

Topics

Resources

Stars

Watchers

Forks

Packages

No packages published

Languages