RecyclerView封装——可以直接项目中使用,拿走不谢!

前言

很久没有发表从零开始搭建android框架系列这个系列的文章了 。由于最近工作确实有点忙碌,也在脚踏实地的花时间研究android方面自己很多不懂的东西。但是写博客确实是一个坚持不懈和自我提高的过程,也希望在保持文章更新的同时能够保持文章的质量 。之前翻译了一些文章,有兴趣的小伙伴可以去看一下。今天这篇文章来谈一谈RecyclerView的封装,对RecyclerView的一些使用点进行总结,以及如何将RecyclerView的adapter进一步简化。平时开发使用的RecyclerView Adapter是来自鸿洋大神的为RecyclerView打造通用Adapter 让RecyclerView更加好用以及对应的github项目baseAdapter github.但是有个问题是他这篇文章写的时间比较早,项目一直在维护,所以本篇文章也算是对整个项目的思路的再梳理。 
刚好解决了昨天在鸿洋博客下看到的这个小问题。哈哈。希望对大家有帮助。 
RecyclerView封装——可以直接项目中使用,拿走不谢!_第1张图片

项目结构

首先看看我的项目结构,项目分为common 和module模块,这里对之前整个项目的框架进行了改造,没有了之前的library,取而代之的是将所有公用组件放在了common包中,这是每个项目都可以copy使用的部分。在module包中就是具体每个项目的每个模块。比如这个示例项目中,包含 
RecyclerView封装——可以直接项目中使用,拿走不谢!_第2张图片

recyclerView组件作为每个项目中都可以使用的组件,这里放在common-widgets-recyclerview包下。

RecyclerView封装——可以直接项目中使用,拿走不谢!_第3张图片

这里可以看到的recyclerView组件这里添加了adapter,base,divider,section,utils,wrapper包。下面来进行深入的讲解以及怎样在项目开发中进行使用吧。

RecyclerView封装——可以直接项目中使用,拿走不谢!_第4张图片

RecyclerView基础

RecyclerView is a more advanced and flexible version of ListView. This widget is a container for large sets of views that can be recycled and scrolled very efficiently. Use the RecyclerView widget when you have lists with elements that change dynamically.  
RecyclerView 是Android L版本中新添加的一个用来取代ListView的SDK,它的灵活性与可替代性比listview更好。RecyclerView与ListView原理是类似的:都是仅仅维护少量的View并且可以展示大量的数据集。在RecyclerView标准化了ViewHolder类似于ListView中convertView用来做视图缓存。

请直接参考 Android RecyclerView 使用完全解析 体验艺术般的控件

ViewHolder

ViewHolder是google在优化ListView性能的技巧上就提到的,虽然google并没有强制使用,但事实上它已经成为ListView的编写规范。在RecyclerView上,ViewHolder的使用成为了一种强制手段了。接下来对封装的ViewHolder进行分析: 
首先来看看ViewHolder的用法,这是一个简单的获取String数组并展现到TextView上。通过数组的大小来创建item的数量。

public class MyAdapter extends RecyclerView.Adapter<MyAdapter.ViewHolder> {
    public String[] datas = null;
    public MyAdapter(String[] datas) {
        this.datas = datas;
    }
    //创建新View,被LayoutManager所调用
    @Override
    public ViewHolder onCreateViewHolder(ViewGroup viewGroup, int viewType) {
        View view = LayoutInflater.from(viewGroup.getContext()).inflate(R.layout.item,viewGroup,false);
        ViewHolder vh = new ViewHolder(view);
        return vh;
    }
    //将数据与界面进行绑定的操作
    @Override
    public void onBindViewHolder(ViewHolder viewHolder, int position) {
        viewHolder.mTextView.setText(datas[position]);
    }
    //获取数据的数量
    @Override
    public int getItemCount() {
        return datas.length;
    }
    //自定义的ViewHolder,持有每个Item的的所有界面元素
    public static class ViewHolder extends RecyclerView.ViewHolder {
        public TextView mTextView;
        public ViewHolder(View view){
        super(view);
            mTextView = (TextView) view.findViewById(R.id.text);
        }
    }
}
   
   
   
   
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31

当然这里只是简单的一个TextView,但是当数据多起来之后,很多TextView,ImageView,以及代码段

View view = LayoutInflater.from(viewGroup.getContext()).inflate(R.layout.item,viewGroup,false); 
ViewHolder vh = new ViewHolder(view);
   
   
   
   
  • 1
  • 2

都可以进行稍微修改。现在ViewHolder修改如下:



public class ViewHolder extends RecyclerView.ViewHolder {
    private SparseArray mViews;
    private View mConvertView;
    private Context mContext;

    ImageLoaderUtil imageLoaderUtil;


    public ViewHolder(Context context, View itemView) {
        super(itemView);
        mContext = context;
        mConvertView = itemView;
        mViews = new SparseArray<>();
        imageLoaderUtil = new ImageLoaderUtil();
    }


    public static ViewHolder createViewHolder(Context context, View itemView) {
        return new ViewHolder(context, itemView);
    }

    public static ViewHolder createViewHolder(Context context,
                                              ViewGroup parent, int layoutId) {
        View itemView = LayoutInflater.from(context).inflate(layoutId, parent,
                false);
        return new ViewHolder(context, itemView);
    }


    /**
     * 通过viewId获取控件
     *
     * @param viewId
     * @return
     */
    public  T getView(int viewId) {
        View view = mViews.get(viewId);
        if (view == null) {
            view = mConvertView.findViewById(viewId);
            mViews.put(viewId, view);
        }
        return (T) view;
    }

    public View getConvertView() {
        return mConvertView;
    }


    /****以下为辅助方法*****/

    /**
     * 设置TextView的值
     *
     * @param viewId
     * @param text
     * @return
     */
    public ViewHolder setText(int viewId, String text) {
        TextView tv = getView(viewId);
        tv.setText(text);
        return this;
    }

    public ViewHolder setImageResource(int viewId, int resId) {
        ImageView view = getView(viewId);
        view.setImageResource(resId);
        return this;
    }

    public ViewHolder setImageUrl(int viewId, String url) {
        ImageView view = getView(viewId);
        ImageLoader.Builder builder = new ImageLoader.Builder();
        ImageLoader img = builder.url(url)
                .imgView(view).strategy(ImageLoaderUtil.LOAD_STRATEGY_ONLY_WIFI).build();
        imageLoaderUtil.loadImage(mContext, img);
        return this;
    }

    public ViewHolder setImageBitmap(int viewId, Bitmap bitmap) {
        ImageView view = getView(viewId);
        view.setImageBitmap(bitmap);
        return this;
    }

    public ViewHolder setImageDrawable(int viewId, Drawable drawable) {
        ImageView view = getView(viewId);
        view.setImageDrawable(drawable);
        return this;
    }
......
......

    /**
     * 关于事件的
     */
    public ViewHolder setOnClickListener(int viewId,
                                         View.OnClickListener listener) {
        View view = getView(viewId);
        view.setOnClickListener(listener);
        return this;
    }

......


}

   
   
   
   
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107
  • 108
  • 109
  • 110

这里需要关注的是getView方法,直接返回当前view的类型。 
所以我们可以在使用viewholder的时候

holder.setText(R.id.text_view, "text");
   
   
   
   
  • 1

就完成了textView的setText操作。而没有进行类型转换。当然这里省去了findViewById的步骤的同时,使用private SparseArray mViews 进行所有view的保存,也就是在牺牲一定内存性能的情况下,确保了代码的整洁性。还需要关注上面的

    public ViewHolder setImageUrl(int viewId, String url) {
        ImageView view = getView(viewId);
        ImageLoader.Builder builder = new ImageLoader.Builder();
        ImageLoader img = builder.url(url)
                .imgView(view).strategy(ImageLoaderUtil.LOAD_STRATEGY_ONLY_WIFI).build();
        imageLoaderUtil.loadImage(mContext, img);
        return this;
    }
   
   
   
   
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

这里只需要传入ImageView的id,和url就可以解析网络图片并完成加载。使用的是Glide进行图片加载。具体可以参考之前的文章网络图片加载的封装.这样封装还有一个好处是当你遇到奇葩的服务器返回字段,比如说我们前段时间遇到的每次返回的textView的text都是有问题的,需要我们自己处理,比如说时间需要截取并返回刚刚,几小时前,我们都可以在ViewHolder进行统一的处理,而不需要在每个数据获取的时候进行处理。

多Item布局实现

这也是我们使用RecyclerView和ListView中过程中经常遇到的问题。看看网易新闻的列表样式,顶部大图,标题+三张图片,标题+左侧图片,视频样式,广告样式……. 这种情况我们怎么便捷快速处理呢?

RecyclerView封装——可以直接项目中使用,拿走不谢!_第5张图片

看看通常处理itemView的type类型不同的方法:

public class MyAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
    class ViewHolder0 extends RecyclerView.ViewHolder {
        ...
    }

    class ViewHolder2 extends RecyclerView.ViewHolder {
        ...
    }

    @Override
    public int getItemViewType(int position) {
        // Just as an example, return 0 or 2 depending on position
        // Note that unlike in ListView adapters, types don't have to be contiguous
        return position % 2 * 2;
    }

    @Override
    public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
         switch (viewType) {
             case 0: return new ViewHolder0(...);
             case 2: return new ViewHolder2(...);
             ...
         }
    }
}
   
   
   
   
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25

这里对方法getItemViewType()进行重写, 并且在 onCreateViewHolder()针对不同的viewType进行不同的ViewHolder创建。 
同时,这样使用不同的type 来处理不同的位置的数据,也能解决ListView中经常遇到的一个问题,那就是header和footer 的view的添加。我们只需要针对首尾位置进行itemViewType 的处理并且返回header和footer的view就行了。

这里也对这种情况进行了处理的封装: 
看看实际项目中的效果,一个adapter就完成了所有的不同的item类型操作。 
整个类继承自MultiItemTypeAdapter

/**
 * Created by Anthony
 * 

* */ public class NewsMultiAdapter extends MultiItemTypeAdapter<NewsItem> { public NewsMultiAdapter(Context context) { super(context); addItemViewDelegate(new TodayTopicDelegate());// docType = 5, 今日头条样式 addItemViewDelegate(new JustTitleDelegate());// docType = 4, 纯文字样式 addItemViewDelegate(new OtherTypeDelegate());// docType = 0/1, 默认左侧图片 + 右侧标题,描述字段样式 // addItemViewDelegate(new BigPicTypeDelegate()); //docType = 2, 顶部标题 + 一张大横图样式 } /* docType = 5, 今日头条样式 docType = 0, 默认左侧图片 + 右侧标题,描述字段样式 docType = 1, 顶部标题 + 三张图片样式 docType = 2, 顶部标题 + 一张大横图样式 docType = 3, 默认样式 + 图集图标 -->点击进入图集细览详情 docType = 4, 纯文字样式 docType = 5, 今日头条样式 docType = 6, 专题样式 */ public class TodayTopicDelegate implements ItemViewDelegate<NewsItem> { @Override public int getItemViewLayoutId() { return R.layout.gz_tab1_item_today_topic; } @Override public boolean isForViewType(NewsItem item, int position) { return item.getType() == 5; } @Override public void convert(ViewHolder holder, NewsItem item, int position) { holder.setText(R.id.tv_title_center, item.getTitle()); holder.setText(R.id.tv_news_date, item.getTime()); } } public class OtherTypeDelegate implements ItemViewDelegate<NewsItem> { @Override public int getItemViewLayoutId() { return R.layout.gz_tab1_item_normal_news; } @Override public boolean isForViewType(NewsItem item, int position) { return item.getType() == 0; } @Override public void convert(ViewHolder holder, NewsItem item, int position) { holder.setText(R.id.tv_title_center, item.getTitle()); holder.setText(R.id.tv_news_source, item.getSummary()); holder.setText(R.id.tv_news_date, item.getTime()); if (item.getImgs() != null) { String url = item.getImgs().get(0); holder.setImageUrlInGZ(R.id.img_news_image, url); } } } public class JustTitleDelegate implements ItemViewDelegate<NewsItem> { @Override public int getItemViewLayoutId() { return R.layout.gz_tab1_item_just_title; } @Override public boolean isForViewType(NewsItem item, int position) { return item.getType() == 4; } @Override public void convert(ViewHolder holder, NewsItem item, int position) { holder.setText(R.id.tv_title_center, item.getTitle()); holder.setText(R.id.tv_news_date, item.getDate()); } }

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90

来看看MultiItemTypeAdapter

/**
 * Created by zhy on 16/4/9.
 */
public class MultiItemTypeAdapter<T> extends RecyclerView.Adapter<ViewHolder>
{
    protected Context mContext;
    protected List mDatas;

    protected ItemViewDelegateManager mItemViewDelegateManager;
    protected OnItemClickListener mOnItemClickListener;
    public int offset = 0;

    public MultiItemTypeAdapter(Context context, List datas)
    {
        mContext = context;
        mDatas = datas;
        mItemViewDelegateManager = new ItemViewDelegateManager();
    }

    public MultiItemTypeAdapter(Context context)
    {
        this(context, new ArrayList());
    }

    @Override
    public int getItemViewType(int position)
    {
        if (!useItemViewDelegateManager()) return super.getItemViewType(position);
        return mItemViewDelegateManager.getItemViewType(mDatas.get(position), position);
    }


    @Override
    public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType)
    {
        int layoutId = mItemViewDelegateManager.getItemViewLayoutId(viewType);
        ViewHolder holder = ViewHolder.createViewHolder(mContext, parent, layoutId);
        setListener(parent, holder, viewType);
        return holder;
    }

    public void convert(ViewHolder holder, T t)
    {
        mItemViewDelegateManager.convert(holder, t, holder.getAdapterPosition());
    }

    protected boolean isEnabled(int viewType)
    {
        return true;
    }


    protected void setListener(final ViewGroup parent, final ViewHolder viewHolder, int viewType)
    {
        if (!isEnabled(viewType)) return;
        viewHolder.getConvertView().setOnClickListener(new View.OnClickListener()
        {
            @Override
            public void onClick(View v)
            {
                if (mOnItemClickListener != null)
                {
                    int position = viewHolder.getAdapterPosition();
                    mOnItemClickListener.onItemClick(v, viewHolder, mDatas.get(position - offset), position);
                }
            }
        });

        viewHolder.getConvertView().setOnLongClickListener(new View.OnLongClickListener()
        {
            @Override
            public boolean onLongClick(View v)
            {
                if (mOnItemClickListener != null)
                {
                    int position = viewHolder.getAdapterPosition();
                    return mOnItemClickListener.onItemLongClick(v, viewHolder, mDatas.get(position - offset), position);
                }
                return false;
            }
        });
    }

    @Override
    public void onBindViewHolder(ViewHolder holder, int position)
    {
        convert(holder, mDatas.get(position));
    }

    @Override
    public int getItemCount()
    {
        int itemCount = mDatas.size();
        return itemCount;
    }


    public List getDatas()
    {
        return mDatas;
    }

    public MultiItemTypeAdapter addItemViewDelegate(ItemViewDelegate itemViewDelegate)
    {
        mItemViewDelegateManager.addDelegate(itemViewDelegate);
        return this;
    }

    public MultiItemTypeAdapter addItemViewDelegate(int viewType, ItemViewDelegate itemViewDelegate)
    {
        mItemViewDelegateManager.addDelegate(viewType, itemViewDelegate);
        return this;
    }

    protected boolean useItemViewDelegateManager()
    {
        return mItemViewDelegateManager.getItemViewDelegateCount() > 0;
    }

    public interface OnItemClickListener<T>
    {
        void onItemClick(View view, RecyclerView.ViewHolder holder, T o, int position);

        boolean onItemLongClick(View view, RecyclerView.ViewHolder holder, T o, int position);
    }

    public void setOnItemClickListener(OnItemClickListener onItemClickListener)
    {
        this.mOnItemClickListener = onItemClickListener;
    }

    public void addDataAll(List data) {
        mDatas.addAll(data);
    }

    public void clearData() {
        mDatas.clear();
    }
}

   
   
   
   
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107
  • 108
  • 109
  • 110
  • 111
  • 112
  • 113
  • 114
  • 115
  • 116
  • 117
  • 118
  • 119
  • 120
  • 121
  • 122
  • 123
  • 124
  • 125
  • 126
  • 127
  • 128
  • 129
  • 130
  • 131
  • 132
  • 133
  • 134
  • 135
  • 136
  • 137
  • 138
  • 139
  • 140

MultiItemTypeAdapter,这里主要完成了List形式添加数据,数据类型使用泛型操作,只需要在构造函数中,或者public方法 addDataAll就可以添加列表类型数据。利用ItemViewDelegateManager完成不同类型type的管理.而添加不同的type是对接口ItemViewDelegate的实现。ItemViewDelegateManager起到了一个中间管理者和代理者的作用。具体看下面的代码:

/**
 * Created by zhy on 16/6/22.
 */
public interface ItemViewDelegate<T>
{

    int getItemViewLayoutId();

    boolean isForViewType(T item, int position);

    void convert(ViewHolder holder, T t, int position);

}

   
   
   
   
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
package com.app.gzgov.common.widgets.recyclerview.base;

import android.support.v4.util.SparseArrayCompat;


/**
 * Created by zhy on 16/6/22.
 */
public class ItemViewDelegateManager<T>
{
    SparseArrayCompat> delegates = new SparseArrayCompat();

    public int getItemViewDelegateCount()
    {
        return delegates.size();
    }

    public ItemViewDelegateManager addDelegate(ItemViewDelegate delegate)
    {
        int viewType = delegates.size();
        if (delegate != null)
        {
            delegates.put(viewType, delegate);
            viewType++;
        }
        return this;
    }

    public ItemViewDelegateManager addDelegate(int viewType, ItemViewDelegate delegate)
    {
        if (delegates.get(viewType) != null)
        {
            throw new IllegalArgumentException(
                    "An ItemViewDelegate is already registered for the viewType = "
                            + viewType
                            + ". Already registered ItemViewDelegate is "
                            + delegates.get(viewType));
        }
        delegates.put(viewType, delegate);
        return this;
    }

    public ItemViewDelegateManager removeDelegate(ItemViewDelegate delegate)
    {
        if (delegate == null)
        {
            throw new NullPointerException("ItemViewDelegate is null");
        }
        int indexToRemove = delegates.indexOfValue(delegate);

        if (indexToRemove >= 0)
        {
            delegates.removeAt(indexToRemove);
        }
        return this;
    }

    public ItemViewDelegateManager removeDelegate(int itemType)
    {
        int indexToRemove = delegates.indexOfKey(itemType);

        if (indexToRemove >= 0)
        {
            delegates.removeAt(indexToRemove);
        }
        return this;
    }

    public int getItemViewType(T item, int position)
    {
        int delegatesCount = delegates.size();
        for (int i = delegatesCount - 1; i >= 0; i--)
        {
            ItemViewDelegate delegate = delegates.valueAt(i);
            if (delegate.isForViewType( item, position))
            {
                return delegates.keyAt(i);
            }
        }
        throw new IllegalArgumentException(
                "No ItemViewDelegate added that matches position=" + position + " in data source");
    }

    public void convert(ViewHolder holder, T item, int position)
    {
        int delegatesCount = delegates.size();
        for (int i = 0; i < delegatesCount; i++)
        {
            ItemViewDelegate delegate = delegates.valueAt(i);

            if (delegate.isForViewType( item, position))
            {
                delegate.convert(holder, item, position);
                return;
            }
        }
        throw new IllegalArgumentException(
                "No ItemViewDelegateManager added that matches position=" + position + " in data source");
    }


    public int getItemViewLayoutId(int viewType)
    {
        return delegates.get(viewType).getItemViewLayoutId();
    }

    public int getItemViewType(ItemViewDelegate itemViewDelegate)
    {
        return delegates.indexOfValue(itemViewDelegate);
    }
}
   
   
   
   
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107
  • 108
  • 109
  • 110
  • 111

这里也就解决了多种itemViewType的问题。实现了方便的添加不同的类型的item数据。泛型数据降低了代码的耦合度。

一种item布局的实现:

这里提供一种item布局,就只需要对MultiItemTypeAdapter进行限定一种layout类型。并且isForViewType方法返回true,代表着始终返回当前的layout。

那么对于只有一种类型的列表数据

/**
 * Created by zhy on 16/4/9.
 */
public abstract class CommonAdapter<T> extends MultiItemTypeAdapter<T> {
    protected Context mContext;
    protected int mLayoutId;
    protected List mDatas;
    protected LayoutInflater mInflater;

    public CommonAdapter(final Context context, final int layoutId) {
        this(context, layoutId, new ArrayList());
    }

    public CommonAdapter(final Context context, final int layoutId, List datas) {
        super(context, datas);
        mContext = context;
        mInflater = LayoutInflater.from(context);
        mLayoutId = layoutId;
        mDatas = datas;

        addItemViewDelegate(new ItemViewDelegate() {
            @Override
            public int getItemViewLayoutId() {
                return layoutId;
            }

            @Override
            public boolean isForViewType(T item, int position) {
                return true;
            }

            @Override
            public void convert(ViewHolder holder, T t, int position) {
                CommonAdapter.this.convert(holder, t, position);
            }
        });
    }

    protected abstract void convert(ViewHolder holder, T t, int position);
}
   
   
   
   
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40

具体的新闻类型:


public class NewsSingleAdapter extends CommonAdapter<NewsItem> {
    public NewsSingleAdapter(Context context) {
        super(context, R.layout.prj_list_item_news);
    }

    @Override
    protected void convert(ViewHolder holder, NewsItem item, int position) {
        holder.setText(R.id.tv_news_title, item.getTitle());
        holder.setText(R.id.tv_news_summary, item.getSummary());
        holder.setText(R.id.tv_news_date, item.getTime());
        holder.setImageUrl(R.id.img_news_image,item.getImgs().get(0));
    }
}
   
   
   
   
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14

这里也就实现了单一的列表形式,比如网易新闻的专题样式: 
RecyclerView封装——可以直接项目中使用,拿走不谢!_第6张图片

加载更多以及header 和footer的添加

这里直接参加wrapper包中几个类, 
RecyclerView封装——可以直接项目中使用,拿走不谢!_第7张图片 
这里是对不同的item的type 类型进行控制,从而得到了不同的RecyclerView的样式。具体可以参考我的MVPCommon中的代码。

/**
 * Created by zhy on 16/6/23.
 */
public class HeaderAndFooterWrapper extends RecyclerView.Adapter
{
    private static final int BASE_ITEM_TYPE_HEADER = 100000;
    private static final int BASE_ITEM_TYPE_FOOTER = 200000;

    private SparseArrayCompat mHeaderViews = new SparseArrayCompat<>();
    private SparseArrayCompat mFootViews = new SparseArrayCompat<>();

    private RecyclerView.Adapter mInnerAdapter;
    private RecyclerView.Adapter mNotifyAdapter;

    public HeaderAndFooterWrapper(RecyclerView.Adapter adapter)
    {
        mInnerAdapter = adapter;
    }

    @Override
    public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType)
    {
        if (mHeaderViews.get(viewType) != null)
        {
            ViewHolder holder = ViewHolder.createViewHolder(parent.getContext(), mHeaderViews.get(viewType));
            return holder;

        } else if (mFootViews.get(viewType) != null)
        {
            ViewHolder holder = ViewHolder.createViewHolder(parent.getContext(), mFootViews.get(viewType));
            return holder;
        }
        return mInnerAdapter.onCreateViewHolder(parent, viewType);
    }

    @Override
    public int getItemViewType(int position)
    {
        if (isHeaderViewPos(position))
        {
            return mHeaderViews.keyAt(position);
        } else if (isFooterViewPos(position))
        {
            return mFootViews.keyAt(position - getHeadersCount() - getRealItemCount());
        }
        return mInnerAdapter.getItemViewType(position - getHeadersCount());
    }

    private int getRealItemCount()
    {
        return mInnerAdapter.getItemCount();
    }


    @Override
    public void onBindViewHolder(RecyclerView.ViewHolder holder, int position)
    {
        if (isHeaderViewPos(position))
        {
            return;
        }
        if (isFooterViewPos(position))
        {
            return;
        }
        mInnerAdapter.onBindViewHolder(holder, position - getHeadersCount());
    }

    @Override
    public int getItemCount()
    {
        return getHeadersCount() + getFootersCount() + getRealItemCount();
    }

    @Override
    public void onAttachedToRecyclerView(RecyclerView recyclerView)
    {
        mNotifyAdapter = recyclerView.getAdapter();
        WrapperUtils.onAttachedToRecyclerView(mInnerAdapter, recyclerView, new WrapperUtils.SpanSizeCallback()
        {
            @Override
            public int getSpanSize(GridLayoutManager layoutManager, GridLayoutManager.SpanSizeLookup oldLookup, int position)
            {
                int viewType = getItemViewType(position);
                if (mHeaderViews.get(viewType) != null)
                {
                    return layoutManager.getSpanCount();
                } else if (mFootViews.get(viewType) != null)
                {
                    return layoutManager.getSpanCount();
                }
                if (oldLookup != null)
                    return oldLookup.getSpanSize(position);
                return 1;
            }
        });
    }

    @Override
    public void onViewAttachedToWindow(RecyclerView.ViewHolder holder)
    {
        mInnerAdapter.onViewAttachedToWindow(holder);
        int position = holder.getLayoutPosition();
        if (isHeaderViewPos(position) || isFooterViewPos(position))
        {
            WrapperUtils.setFullSpan(holder);
        }
    }

    private boolean isHeaderViewPos(int position)
    {
        return position < getHeadersCount();
    }

    private boolean isFooterViewPos(int position)
    {
        return position >= getHeadersCount() + getRealItemCount();
    }

    public void addHeaderView(View view)
    {
        int key = findHeaderKeyByView(view);
        if (key == -1) {
            mHeaderViews.put(mHeaderViews.size() + BASE_ITEM_TYPE_HEADER, view);
            if (mNotifyAdapter != null)
                mNotifyAdapter.notifyDataSetChanged();

            if (mInnerAdapter instanceof MultiItemTypeAdapter) {
                ((MultiItemTypeAdapter) mInnerAdapter).offset += 1;
            }
        }
    }

    public void addFootView(View view)
    {
        mFootViews.put(mFootViews.size() + BASE_ITEM_TYPE_FOOTER, view);
    }

    public int getHeadersCount()
    {
        return mHeaderViews.size();
    }

    public int getFootersCount()
    {
        return mFootViews.size();
    }

    public void deleteHeaderView(View view)
    {
//        if (mHeaderViews.size() > position && position >=0 ) {
//            View v = mHeaderViews.get(position + BASE_ITEM_TYPE_HEADER, null);
//            if (v != null) {
//                mHeaderViews.remove(position + BASE_ITEM_TYPE_HEADER);
//                if (mInnerAdapter instanceof MultiItemTypeAdapter) {
//                    ((MultiItemTypeAdapter) mInnerAdapter).offset -= 1;
//                }
//                if (mNotifyAdapter != null)
//                    mNotifyAdapter.notifyDataSetChanged();
//            }
//        }

//        for(int i=0; i
//            int key = mHeaderViews.keyAt(i);
//            if(mHeaderViews.get(key) == view) {
//                mHeaderViews.remove(key);
//                if (mInnerAdapter instanceof MultiItemTypeAdapter) {
//                    ((MultiItemTypeAdapter) mInnerAdapter).offset -= 1;
//                }
//                if (mNotifyAdapter != null)
//                    mNotifyAdapter.notifyDataSetChanged();
//                break;
//            }
//        }

        int key = findHeaderKeyByView(view);
        if (key != -1) {
            mHeaderViews.remove(key);
            if (mInnerAdapter instanceof MultiItemTypeAdapter) {
                ((MultiItemTypeAdapter) mInnerAdapter).offset -= 1;
            }
            if (mNotifyAdapter != null)
                mNotifyAdapter.notifyDataSetChanged();
        }
    }

    public int findHeaderKeyByView(View view) {
        for(int i=0; iint key = mHeaderViews.keyAt(i);
            if(mHeaderViews.get(key) == view) {
                return key;
            }
        }

        return -1;
    }
}
   
   
   
   
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107
  • 108
  • 109
  • 110
  • 111
  • 112
  • 113
  • 114
  • 115
  • 116
  • 117
  • 118
  • 119
  • 120
  • 121
  • 122
  • 123
  • 124
  • 125
  • 126
  • 127
  • 128
  • 129
  • 130
  • 131
  • 132
  • 133
  • 134
  • 135
  • 136
  • 137
  • 138
  • 139
  • 140
  • 141
  • 142
  • 143
  • 144
  • 145
  • 146
  • 147
  • 148
  • 149
  • 150
  • 151
  • 152
  • 153
  • 154
  • 155
  • 156
  • 157
  • 158
  • 159
  • 160
  • 161
  • 162
  • 163
  • 164
  • 165
  • 166
  • 167
  • 168
  • 169
  • 170
  • 171
  • 172
  • 173
  • 174
  • 175
  • 176
  • 177
  • 178
  • 179
  • 180
  • 181
  • 182
  • 183
  • 184
  • 185
  • 186
  • 187
  • 188
  • 189
  • 190
  • 191
  • 192
  • 193
  • 194
  • 195
  • 196
  • 197

这里的header和footer没有个数的限制。

添加section分区操作:

现在需求又来了 。需要对RecyclerView中的item进行分区操作,就比如说微信以B开头的姓名都放在B这个分区下,以C开头的名字,都在C这个分区下。比如说京东的这个界面 
列表数据里面添加了分区。那么怎么操作呢? 
RecyclerView封装——可以直接项目中使用,拿走不谢!_第8张图片 
这里对开源库SectionedRecyclerViewAdapter做了集成。并且添加的上面的ViewHolder,简化onCreateViewHolder中的数据绑定操作。 
也就是代码中的recyclerview-section包中的部分。 

Section的使用:

1) 创建自定义 Section 类集成自 StatelessSection

    public class WeiboGridSection extends StatelessSection {
        private final List  list;

        public WeiboGridSection(List list) {
            super(R.layout.grid_item);
            this.list = list;
        }

        @Override
        public int getContentItemsTotal() {
            return list.getItems().size();
        }

        @Override
        public ViewHolder getItemViewHolder(View view, int viewType) {
            return new ViewHolder(mContext, view);
        }

        @Override
        public void onBindItemViewHolder(ViewHolder holder, final int position) {

                final NewsItem newsItem = list.get(position);
                String itemImgUrl = newsItem.getImages().get(0);
                final String name = newsItem.getTitle();
                holder.setImageUrl(R.id.grid_item_iv, itemImgUrl);
                holder.setText(R.id.grid_item_tv, name);
                holder.getConvertView().setOnClickListener(new View.OnClickListener() {
                    @Override
                    public void onClick(View v) {
                        Intent intent = new Intent(getActivity(), WebViewCommentActivity.class);
                        intent.putExtra(WebViewCommentActivity.WEB_VIEW_ITEM, newsItem);
                        startActivity(intent);
                    }
                });
            }
        }
        @Override
        public ViewHolder getHeaderViewHolder(Context context, View view) {
            return new ViewHolder(mContext, view);
        }

        @Override
        public void onBindHeaderViewHolder(ViewHolder holder) {
            holder.setText(R.id.section_header_tv, "微博关注");
            holder.setImageResource(R.id.section_header_iv, R.mipmap.wb_focus);

        }

    }


   
   
   
   
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51

2) 添加section到adapter,注意这里是SectionRVAdapter

// Create an instance of SectionedRecyclerViewAdapter 
SectionRVAdapter sectionAdapter = new SectionRVAdapter();

// Add your Sections
sectionAdapter.addSection(new MySection());

// Set up your RecyclerView with the SectionRVAdapter
RecyclerView recyclerView = (RecyclerView) findViewById(R.id.recyclerview);
recyclerView.setLayoutManager(new LinearLayoutManager(getContext()));
recyclerView.setAdapter(sectionAdapter);
   
   
   
   
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10

看看界面效果 
RecyclerView封装——可以直接项目中使用,拿走不谢!_第9张图片

Section的代码实现

整个代码由于是对RecyclerView.Adapter封装。所以需要关注的方法自然是getItemViewType,onBindViewHolder,createViewHoldergetItemCount四个方法,下面以这四个方法为切入点进行分析: 
初始化需要关注的是这里使用hashMap对section进行存储。也就实现了后面的根据section的添加顺序依次展示Section到RecyclerView中。 
RecyclerView封装——可以直接项目中使用,拿走不谢!_第10张图片 
onCreateViewHolder完成的是ViewHolder的创建,每一个section分为头部header,底部footer。以及中间部分,中间布局可以有Loading/Loaded/Failed三种状态分别对应加载,加载成功,失败界面。注意这里的状态都分别对应于每个section里面,而不是整个RecyclerView.

也就是说一个RecyclerView可以由多个Section组成,一个Section最多只能有一个Header和Footer,Section由多个RecyclerView的item条目组成。每个Section中间可以有三种状态。Loading/Loaded/Failed三种状态分别对应加载,加载成功,失败界面。

下面是来自SectionedRecyclerViewAdapter的界面效果 
RecyclerView封装——可以直接项目中使用,拿走不谢!_第11张图片

  @Override
    public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
        ViewHolder viewHolder = null;
        View view = null;

        for (Map.Entry entry : sectionViewTypeNumbers.entrySet()) {
            if (viewType >= entry.getValue() && viewType < entry.getValue() + VIEW_TYPE_QTY) {

                Section section = sections.get(entry.getKey());
                int sectionViewType = viewType - entry.getValue();

                switch (sectionViewType) {
                    case VIEW_TYPE_HEADER: {
                        Integer resId = section.getHeaderResourceId();

                        if (resId == null)
                            throw new NullPointerException("Missing 'header' resource id");

                        view = LayoutInflater.from(parent.getContext()).inflate(resId, parent, false);
                        // get the header viewholder from the section
                        viewHolder = section.getHeaderViewHolder(mContext,view);
                        break;
                    }
                    case VIEW_TYPE_FOOTER: {
                        Integer resId = section.getFooterResourceId();

                        if (resId == null)
                            throw new NullPointerException("Missing 'footer' resource id");

                        view = LayoutInflater.from(parent.getContext()).inflate(resId, parent, false);
                        // get the footer viewholder from the section
                        viewHolder = section.getFooterViewHolder(mContext,view);
                        break;
                    }
                    case VIEW_TYPE_ITEM_LOADED: {
                        view = LayoutInflater.from(parent.getContext()).inflate(section.getItemResourceId(), parent, false);
                        // get the item viewholder from the section
                        viewHolder = section.getItemViewHolder(view,viewType);
                        break;
                    }
                    case VIEW_TYPE_LOADING: {
                        Integer resId = section.getLoadingResourceId();

                        if (resId == null)
                            throw new NullPointerException("Missing 'loading state' resource id");

                        view = LayoutInflater.from(parent.getContext()).inflate(resId, parent, false);
                        // get the loading viewholder from the section
                        viewHolder = section.getLoadingViewHolder(mContext,view);
                        break;
                    }
                    case VIEW_TYPE_FAILED: {
                        Integer resId = section.getFailedResourceId();

                        if (resId == null)
                            throw new NullPointerException("Missing 'failed state' resource id");

                        view = LayoutInflater.from(parent.getContext()).inflate(resId, parent, false);
                        // get the failed load viewholder from the section
                        viewHolder = section.getFailedViewHolder(mContext,view);
                        break;
                    }
                    default:
                        throw new IllegalArgumentException("Invalid viewType");
                }
            }
        }

        return viewHolder;
    }
   
   
   
   
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70

和前面MultiItemTypeAdapter的实现一样,我们需要根据不同viewType创建不同的viewHolder.但是需要注意的是一个Section是由一组item组成的,所以一个section需要多次的调用onCreateViewHolder进行创建不同的类型的样式。

接下来关注onBindViewHolder方法,通过 
int sectionTotal = section.getSectionItemsTotal();获取到了section的item的数量,并在下方针对每个section的头部header,底部footer,以及中间部分进行操作。并且调用onBindHeaderViewHolder(holder)
onBindFooterViewHolder(holder) 
以及onBindContentViewHolder(holder, getSectionPosition(position))方法,这就是当我们实现Section代码的时候需要实现的方法。section.getSectionItemsTotal()也是我们实现Section的时候提供的section的item的个数。

    @Override
    public void onBindViewHolder(ViewHolder holder, int position) {

        int currentPos = 0;

        for (Map.Entry entry : sections.entrySet()) {
            Section section = entry.getValue();

            // ignore invisible sections
            if (!section.isVisible()) continue;

            int sectionTotal = section.getSectionItemsTotal();

            // check if position is in this section
            if (position >= currentPos && position <= (currentPos + sectionTotal - 1)) {

                if (section.hasHeader()) {
                    if (position == currentPos) {
                        // delegate the binding to the section header
                        getSectionForPosition(position).onBindHeaderViewHolder(holder);
                        return;
                    }
                }

                if (section.hasFooter()) {
                    if (position == (currentPos + sectionTotal - 1)) {
                        // delegate the binding to the section header
                        getSectionForPosition(position).onBindFooterViewHolder(holder);
                        return;
                    }
                }

                // delegate the binding to the section content
                getSectionForPosition(position).onBindContentViewHolder(holder, getSectionPosition(position));
                return;
            }

            currentPos += sectionTotal;
        }

        throw new IndexOutOfBoundsException("Invalid position");
    }
   
   
   
   
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42

接下来关注getItemCount,代表整个RecyclerView的item的个数。当然是所有Section的item的总和,所以代码如下。

RecyclerView封装——可以直接项目中使用,拿走不谢!_第12张图片 
最后需要关注的是方法getItemViewType,这里也就完成了每个Section的五种类型的int返回操作。

   @Override
    public int getItemViewType(int position) {
         /*
         Each Section has 5 "viewtypes":
         1) header
         2) footer
         3) items
         4) loading
         5) load failed
         */
        int currentPos = 0;

        for (Map.Entry entry : sections.entrySet()) {
            Section section = entry.getValue();

            // ignore invisible sections
            if (!section.isVisible()) continue;

            int sectionTotal = section.getSectionItemsTotal();

            // check if position is in this section
            if (position >= currentPos && position <= (currentPos + sectionTotal - 1)) {

                int viewType = sectionViewTypeNumbers.get(entry.getKey());

                if (section.hasHeader()) {
                    if (position == currentPos) {
                        return viewType;
                    }
                }

                if (section.hasFooter()) {
                    if (position == (currentPos + sectionTotal - 1)) {
                        return viewType + 1;
                    }
                }

                switch (section.getState()) {
                    case LOADED:
                        return viewType + 2;
                    case LOADING:
                        return viewType + 3;
                    case FAILED:
                        return viewType + 4;
                    default:
                        throw new IllegalStateException("Invalid state");
                }

            }

            currentPos += sectionTotal;
        }

        throw new IndexOutOfBoundsException("Invalid position");
    }

   
   
   
   
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56

首先关注的是SectionRVAdapter,这里我并没有集成自上面的MultiItemTypeAdapter,应为这里涉及到对RecyclerView.Adapter的封装操作。这里的弊端也就是每一个section甚至整个RecyclerView的itemView都是一个形式。但是多种形式的section我们可以转化为将section也视作一种MultiItemTypeAdapter的item类型就能解决,所以这里也不算问题。这里是SectionRVAdapter的完整代码。至于Section类,主要是对几种方法和view状态,以及所有item的封装,这里不再赘述。直接看代码。

最后针对最近项目中遇到的这个问题,针对不同的布局,比如说下面的这个既有Grid,又有linear的形式。由于之前的问题全部是针对一个RecyclerView的,而一个RecyclerView在调用recyclerView.setLayoutManager()方法的时候,就只能有一个布局方式。好吧,当初我就是为了解决这个问题,后来才发现需要用三个RecyclerView来解决。 
RecyclerView封装——可以直接项目中使用,拿走不谢!_第13张图片

这篇博客就到这里,回过头去,去看看鸿洋写的为RecyclerView打造通用Adapter 让RecyclerView更加好用,以及开源库baseAdapter github相信你一目了然。 
这里就是对baseAdapter github引用到实际项目中以及引入开源库SectionedRecyclerViewAdapter作为实际开发的例子。

你可能感兴趣的:(Android基础)