我需要一个 StaggeredGridLayoutManager 遵循这两个要求:
- 根据每列设置的最小 dp 大小自动计算列数,列扩展以完全适合可用空间(例如:如果我定义 150dp 最小列,并且应用程序在 480dp 宽度的设备上运行,则网格视图将包含 3 列,剩余的 30dp 将被平均分割,这样每列都是 160dp 宽)
- 网格布局必须在方向更改时重新计算跨度计数,因为在纵向和横向模式下适合屏幕的列数通常相距很远
到目前为止,我还没有需要交错网格项,所以我使用了 GridLayoutManager 的扩展来提供这个功能:
import androidx.recyclerview.widget.GridLayoutManager
import androidx.recyclerview.widget.RecyclerView
import androidx.recyclerview.widget.RecyclerView.Recycler
import kotlin.math.max
import kotlin.math.min
class GridAutoFitLayoutManager : GridLayoutManager {
private var mColumnWidth = 0
private var mMaximumColumns: Int
private var mLastCalculatedWidth = -1
@JvmOverloads
constructor(
context: Context?,
columnWidthDp: Int,
maxColumns: Int = 99
) : super(context, 1) //Initially set spanCount to 1, will be changed automatically later.
{
mMaximumColumns = maxColumns
setColumnWidth(columnWidthDp)
}
private fun setColumnWidth(newColumnWidth: Int) {
if (newColumnWidth > 0 && newColumnWidth != mColumnWidth) {
mColumnWidth = newColumnWidth
}
}
override fun onLayoutChildren(
recycler: Recycler,
state: RecyclerView.State
) {
val width = width
val height = height
if (width != mLastCalculatedWidth && mColumnWidth > 0 && width > 0 && height > 0) {
val totalSpace: Int = if (orientation == RecyclerView.VERTICAL) {
width - paddingRight - paddingLeft
} else {
height - paddingTop - paddingBottom
}
val spanCount = min(
mMaximumColumns,
max(1, totalSpace / mColumnWidth)
)
setSpanCount(spanCount)
mLastCalculatedWidth = width
}
super.onLayoutChildren(recycler, state)
}
}
在回收站视图中使用布局管理器:
recyclerView.layoutManager = GridAutoFitLayoutManager(context, resources.getDimension(R.dimen.grid_column_width).toInt())
最后,项目视图布局在宽度上设置为 match_parent,因此它们将占用布局管理器允许的尽可能多的空间。
但是,我无法让它与 StaggeredGridLayoutManager 一起使用。这是我的代码:
import android.content.Context
import android.widget.LinearLayout
import androidx.recyclerview.widget.RecyclerView
import androidx.recyclerview.widget.RecyclerView.Recycler
import androidx.recyclerview.widget.StaggeredGridLayoutManager
import kotlin.math.max
import kotlin.math.min
class GridAutoFitStaggeredLayoutManager : StaggeredGridLayoutManager {
private var mColumnWidth = 0
private var mMaximumColumns: Int
private var mLastCalculatedWidth = -1
@JvmOverloads
constructor(
context: Context?,
columnWidthDp: Int,
maxColumns: Int = 99
) : super(1, LinearLayout.VERTICAL) //Initially set spanCount to 1, will be changed automatically later.
{
mMaximumColumns = maxColumns
setColumnWidth(columnWidthDp)
}
private fun setColumnWidth(newColumnWidth: Int) {
if (newColumnWidth > 0 && newColumnWidth != mColumnWidth) {
mColumnWidth = newColumnWidth
}
}
override fun onLayoutChildren(
recycler: Recycler,
state: RecyclerView.State
) {
val width = width
val height = height
if (width != mLastCalculatedWidth && mColumnWidth > 0 && width > 0 && height > 0) {
val totalSpace: Int = if (orientation == RecyclerView.VERTICAL) {
width - paddingRight - paddingLeft
} else {
height - paddingTop - paddingBottom
}
val spanCount = min(
mMaximumColumns,
max(1, totalSpace / mColumnWidth)
)
setSpanCount(spanCount)
mLastCalculatedWidth = width
}
super.onLayoutChildren(recycler, state)
}
}
加载回收站视图后,我得到以下异常:
java.lang.IllegalStateException:当 RecyclerView 正在计算布局或滚动时无法调用此方法
问题是因为我在解决布局时调用了 setSpanCount(),这不是 GridLayoutManager 的问题,但在 StaggeredGridLayoutManager 中是不允许的。我的问题是,如果我不能在 onLayoutChildren() 期间设置跨度计数,我什么时候可以设置它?
我知道我可以在初始化布局管理器之前计算出适合多少列,并将跨度计数传递给 StaggeredGridLayoutManager 构造函数。但是,这不会对方向更改做出反应,除非我向重新创建布局管理器的活动添加额外的逻辑,我希望避免这种情况。有没有办法让我的 GridAutoFitStaggeredLayoutManager 计算跨度计数并自行处理方向更改?