感谢 TeaOf 的 GridLayoutManager这么用,你可能还真没尝试过 看到了这篇文章我尝试去写了个demo
写在前面
首页我们的首页由于需求的不断变更,从单一的数据源变成了多个数据源,为了不同数据的显示效果,从三种布局慢慢变成了6种布局。所以从一开始的只是头部不同到现在的太多太多的区别,为了更好的区别每一个布局,对自己的想法内容进行总结,写下了这篇。为什么不用vLayout呢,因为如果使用过vLayout那么不用说了,没有使用过的朋友,在使用的时候会有一定的不适应,而且自定义的多布局,在使用上来说会更加友好一点。然后一般实现界面说复杂有点复杂,但有多复杂也没有多复杂,
效果展示
很常见的一种展示效果 轮播模式 标题模式 歌单模式 海报模式 歌曲模式

实现思路
依赖 BaseRecyclerViewAdapterHelper
可能以前来说,第一眼看到这个就是RecyclerView嵌套一个RecyclerView,对于不同的子RecyclerView设置不同的GridLayoutManager,分配不同的spanSize。唉,有转折,到了spanSize,慢慢想到SpanSizeLookup,这样的话是不是只要设置一个GridLayoutManager对于不同的类型设置一行展示的个数,配合多布局就可以不用那么多的嵌套了。
- 对于多布局的封装 通常都是在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)
}
}
- 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直接使用子布局的处理,减少了一层嵌套,虽然目前的手机对于这种嵌套的影响几乎没有太大影响感知,但是自我感觉是不是进行了一次重大的优化处理