实现一个能设置MaxLine的LayoutManager
有时候,我们会遇到这种需求:一个线性的列表布局,当item量很少的时候,就是wrap_content
直接展示完所有item,但是当item数量超过某个数时就要固定高度
,让其变成可滑动展示更多的item。所以我们第一个想法就是用RecyclerView
,应该没人会用ListView
或自己写个自定义ViewGroup
吧。
但是当我们使用RecyclerView
+maxHeight
的时候,会发现其实maxHeight
是不起作用的。
<span><span><span><</span>androidx.recyclerview.widget.RecyclerView</span><span><span>android:</span>id</span><span><span>=</span><span>"</span>@+id/recyclerView<span>"</span></span><span><span>android:</span>layout_width</span><span><span>=</span><span>"</span>match_parent<span>"</span></span><span><span>android:</span>layout_height</span><span><span>=</span><span>"</span>wrap_content<span>"</span></span><span><span>android:</span>maxHeight</span><span><span>=</span><span>"</span>100dp<span>"</span></span> <span>/></span></span>
那么为什么会造成这种原因呢?其实在我之前写的一篇博客也提到过:RecyclerView
将onMeasure
的转交给了LayoutManager
,并且LayoutManager
里面有个isAutoMeasureEnabled()
方法用来配置LayoutManager
是否开启自测量
。我们常用的LinearLayoutManager
或GridLayoutManager
都是默认返回true
的。
博客地址
实现思路
所以实现思路就很简单了:设定一个maxLine
参数,当RecyclerView
的itemCount
小于这个值isAutoMeasureEnabled()
都是返回true
,让LayoutManager
去自测量
。当itemCount
大于maxLine
时就重写onMeasure
方法,在里面去设置RecyclerView
的限定宽高
。
代码实现
class MaxLineLinearLayoutManager : LinearLayoutManager {private var mMaxLine = 0constructor(context: Context?,maxLine: Int) : super(context) {Helper.checkMaxCount(maxLine)this.mMaxLine = maxLine}constructor(context: Context?,orientation: Int,reverseLayout: Boolean,maxLine: Int) : super(context, orientation, reverseLayout) {Helper.checkMaxCount(maxLine)this.mMaxLine = maxLine}override fun onMeasure(recycler: RecyclerView.Recycler,state: RecyclerView.State,widthSpec: Int,heightSpec: Int) {if (itemCount <= mMaxLine || itemCount == 0) {super.onMeasure(recycler, state, widthSpec, heightSpec)return}val child = recycler.getViewForPosition(0)//addView(child)measureChildWithMargins(child, 0, 0)val itemWidth = getDecoratedMeasuredWidth(child)val itemHeight = getDecoratedMeasuredHeight(child)removeAndRecycleView(child, recycler)val widthMode = View.MeasureSpec.getMode(widthSpec)val heightMode = View.MeasureSpec.getMode(heightSpec)var width = 0var height = 0if (orientation == HORIZONTAL) {height = if (heightMode == View.MeasureSpec.EXACTLY) {View.MeasureSpec.getSize(heightSpec)} else {itemHeight}width = itemWidth * mMaxLine} else {width = if (widthMode == View.MeasureSpec.EXACTLY) {View.MeasureSpec.getSize(widthSpec)} else {itemWidth}height = itemHeight * mMaxLine}setMeasuredDimension(width, height)}override fun isAutoMeasureEnabled(): Boolean {if (itemCount <= mMaxLine) {return super.isAutoMeasureEnabled()}return false}}
代码很简单,应该不加注释也能看懂。如果看不懂的可以去看看我之前的那篇分析自定义LayoutManager
的文章。
博客地址
recyclerView.layoutManager = MaxLineLinearLayoutManager(this, maxLine = 3)
源码地址
https://github.com/simplepeng/MaxLineLayoutManager