阅读 1377

当item超过一定数量,如何正确的设置RecyclerView的maxHeight

有时候,我们会遇到这种需求:一个线性的列表布局,当item量很少的时候,就是wrap_content直接展示完所有item,但是当item数量超过某个数时就要固定高度,让其变成可滑动展示更多的item。所以我们第一个想法就是用RecyclerView,应该没人会用ListView或自己写个自定义ViewGroup吧。

但是当我们使用RecyclerView+maxHeight的时候,会发现其实maxHeight是不起作用的。

    <androidx.recyclerview.widget.RecyclerView
        android:id="@+id/recyclerView"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:maxHeight="100dp" />
复制代码

warp_recycler_view.gif

那么为什么会造成这种原因呢?其实在我之前写的一篇博客也提到过:RecyclerViewonMeasure的转交给了LayoutManager,并且LayoutManager里面有个isAutoMeasureEnabled()方法用来配置LayoutManager是否开启自测量。我们常用的LinearLayoutManagerGridLayoutManager都是默认返回true的。

博客地址

实现思路

所以实现思路就很简单了:设定一个maxLine参数,当RecyclerViewitemCount小于这个值isAutoMeasureEnabled()都是返回true,让LayoutManager自测量。当itemCount大于maxLine时就重写onMeasure方法,在里面去设置RecyclerView限定宽高

代码实现

class MaxLineLinearLayoutManager : LinearLayoutManager {

    private var mMaxLine = 0

    constructor(
        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 = 0
        var height = 0

        if (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)
复制代码

maxline_layoutmanager.gif

源码地址

github.com/simplepeng/…