Sometimes, we will encounter the need for a linear list layout where wrap_content displays all items directly when the number of items is small, but when the number of items exceeds a certain number, the height is fixed so that it can slide to show more items. So our first thought was to use RecyclerView, and I don’t think anyone would use ListView or write a custom ViewGroup.
However, when we use RecyclerView+maxHeight, we will find that maxHeight does not work.
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/recyclerView"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:maxHeight="100dp" />
Copy the code
So why does this happen? As I mentioned in a previous blog post: RecyclerView transfers onMeasure to LayoutManager, and There is an isAutoMeasureEnabled () method in LayoutManager to configure whether LayoutManager enables self-measurement. The LinearLayoutManager or GridLayoutManager we commonly use returns true by default.
Blog address
Implementation approach
So the implementation idea is very simple: set a maxLine parameter, when RecyclerView itemCount is less than this value isAutoMeasureEnabled() return true, let LayoutManager to measure. When itemCount is greater than maxLine, override the onMeasure method to set the RecyclerView limit.
Code implementation
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(a): Boolean {
if (itemCount <= mMaxLine) {
return super.isAutoMeasureEnabled()
}
return false}}Copy the code
The code is simple enough that you should be able to understand it without comments. If you don’t understand, check out my previous article analyzing custom LayoutManager.
Blog address
recyclerView.layoutManager = MaxLineLinearLayoutManager(this, maxLine = 3)
Copy the code
The source address
Github.com/simplepeng/…