RecyclerView多布局处理

1,219 阅读4分钟

感谢 TeaOf 的 GridLayoutManager这么用,你可能还真没尝试过 看到了这篇文章我尝试去写了个demo

写在前面

首页我们的首页由于需求的不断变更,从单一的数据源变成了多个数据源,为了不同数据的显示效果,从三种布局慢慢变成了6种布局。所以从一开始的只是头部不同到现在的太多太多的区别,为了更好的区别每一个布局,对自己的想法内容进行总结,写下了这篇。为什么不用vLayout呢,因为如果使用过vLayout那么不用说了,没有使用过的朋友,在使用的时候会有一定的不适应,而且自定义的多布局,在使用上来说会更加友好一点。然后一般实现界面说复杂有点复杂,但有多复杂也没有多复杂,

效果展示

很常见的一种展示效果 轮播模式 标题模式 歌单模式 海报模式 歌曲模式

实现思路

依赖 BaseRecyclerViewAdapterHelper

可能以前来说,第一眼看到这个就是RecyclerView嵌套一个RecyclerView,对于不同的子RecyclerView设置不同的GridLayoutManager,分配不同的spanSize。唉,有转折,到了spanSize,慢慢想到SpanSizeLookup,这样的话是不是只要设置一个GridLayoutManager对于不同的类型设置一行展示的个数,配合多布局就可以不用那么多的嵌套了。

  1. 对于多布局的封装 通常都是在convert方法判断不同的itemType,来进行不同的数据判断,在这里我使用了多个Provider来进行提供布局和数据,通过调用Provider的convert方法来进行设置数据,所以到使用的时候就看起来非常的简洁了
abstract class BaseMultiAdapter<T : MultiItemEntity>
    : BaseMultiItemQuickAdapter<T, ViewHolder>(null) {

    /*** Provider存储 ***/
    private val mTypeProviders = SparseArray<BaseMultiProvider<T>>()

    init {
        // 绑定多布局类型
        this.bindMultiType().forEach {
            addMultiTypeProviders(it)
        }
    }

    /**
     * 使用addMultiTypeProvider方法进行设置
     */
    private fun addMultiTypeProviders(vararg providers: BaseMultiProvider<T>) {
        providers.forEach {
            mTypeProviders.put(it.multiType, it)
            //绑定多布局的类型和布局
            addItemType(it.multiType, it.layoutRes)
        }
    }

    override fun convert(helper: ViewHolder, item: T) {
        // 根据类型获取Provider进行设置数据
        mTypeProviders[helper.itemViewType]?.converts(mContext, helper, item)
    }

    /**
     * 返回MultiProvider集合
     */
    abstract fun bindMultiType(): List<BaseMultiProvider<T>>

}
class GridMultiAdapter : BaseMultiAdapter<MultiEntity>() {
    /**
     * 返回MultiProvider集合
     */
    override fun bindMultiType(): List<BaseMultiProvider<MultiEntity>> {
        return arrayListOf(
            //轮播图模式
            BannerProvider(),
            //标题模式
            TitleProvider(),
            //歌单模式
            ListProvider(),
            //海报模式
            PosterProvider(),
            //歌曲模式
            SongProvider()
        )
    }
}
class PosterProvider :
    BaseMultiProvider<MultiEntity>(GridMulti.TYPE_POSTER, R.layout.item_multi_grid_poster) {

    /**
     * 数据处理
     */
    override fun converts(context: Context, helper: ViewHolder, item: MultiEntity) {
        ImageLoadHelper.loadImage(context, item.img, helper.getView(R.id.iv_cover))
        helper.setText(R.id.tv_title, item.title)
    }
}
  1. RecyclerView配置 首先获取到ui给到的所有布局,计算出显示个数的最小公倍数。例如: 轮播模式 = 1 标题模式 = 1 歌单模式 = 3 海报模式 = 2 歌曲模式 = 1 那么最小公倍数就是6 !!!注意实际布局的展示数量是 spanCount/spanSizeLookup
        with(mData) {
            add(MultiEntity("", url, GridMulti.TYPE_BANNER))
            add(MultiEntity("推荐歌单", "", GridMulti.TYPE_TITLE))
            add(MultiEntity("[华语私人定制]你爱的好歌都在这儿", url, GridMulti.TYPE_LIST))
            add(MultiEntity("90后青春纪念手册", url, GridMulti.TYPE_LIST))
            add(MultiEntity("让你耳朵怀孕的英文歌", url, GridMulti.TYPE_LIST))
            add(MultiEntity("独家放送", "", GridMulti.TYPE_TITLE))
            add(MultiEntity("传奇名作Top10", url, GridMulti.TYPE_POSTER))
            add(MultiEntity("不一样的《年少有为》", url, GridMulti.TYPE_POSTER))
            add(MultiEntity("最新音乐", "", GridMulti.TYPE_TITLE))
            add(MultiEntity("富士山下", url, GridMulti.TYPE_SONG))
            add(MultiEntity("最佳损友", url, GridMulti.TYPE_SONG))
            add(MultiEntity("好久不见", url, GridMulti.TYPE_SONG))
            add(MultiEntity("一丝不挂", url, GridMulti.TYPE_SONG))
            add(MultiEntity("孤独患者", url, GridMulti.TYPE_SONG))
            add(MultiEntity("落花流水", url, GridMulti.TYPE_SONG))
        }
        mRvList.run {
            //设置一排的展示个数
            layoutManager = GridLayoutManager(context, 6)
            //添加粘性头
            addItemDecoration(mStickyDecoration)
            //设置数量  展示数量 = 6 / spanSizeLookup
            mAdapter.setSpanSizeLookup { _, position ->
                when (mAdapter.data[position].type) {
                    GridMulti.TYPE_TITLE,
                    GridMulti.TYPE_SONG -> 6
                    GridMulti.TYPE_POSTER -> 3
                    GridMulti.TYPE_LIST -> 2
                    else -> 6
                }
            }
            //设置数据
            mAdapter.setNewData(mData)
            //设置adapter
            adapter = mAdapter
        }

写在最后

到这里,一个多布局就已经完成了,可以看到这样子进行处理,我自己总结有两点好处,一个是在封装处,对于每一个多布局item,所有的内容都由各自的Provider来进行处理,数据的来龙去脉都可以很清楚的看到,处理起来也会更加方便,代码也不会写在一堆。第二个是对于GridLayoutManager的使用,原先是RecyclerView嵌套RecyclerView,再进行子布局的处理,现在RecyclerView直接使用子布局的处理,减少了一层嵌套,虽然目前的手机对于这种嵌套的影响几乎没有太大影响感知,但是自我感觉是不是进行了一次重大的优化处理