• Stars
    star
    172
  • Rank 221,201 (Top 5 %)
  • Language
    Java
  • Created almost 8 years ago
  • Updated almost 7 years ago

Reviews

There are no reviews yet. Be the first to send feedback to the community and the maintainers!

Repository Details

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

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