动态更改 RecyclerView 的跨度计数和 ModelView 大小

huangapple go评论59阅读模式
英文:

Dynamically Change Span count and ModelView Size for Recycler View

问题

I have an epoxy recycler view that can display an item in the maximum possible size and also want to display the maximum number of items by adjusting span count and item size. For that, I am using this layout manager,

class GridAutoFitLayoutManager(
    context: Context,
    private val minItemSize: Int,
    private val maxItemSize: Int
) : GridLayoutManager(context, 1) {

    override fun onLayoutChildren(recycler: RecyclerView.Recycler, state: RecyclerView.State) {
        updateSpanCount()
        super.onLayoutChildren(recycler, state)
    }

    override fun generateDefaultLayoutParams(): RecyclerView.LayoutParams {
        val layoutParams = super.generateDefaultLayoutParams()
        val itemSize = calculateItemSize()
        layoutParams.width = itemSize
        layoutParams.height = itemSize
        return layoutParams
    }

    override fun generateLayoutParams(c: Context, attrs: android.util.AttributeSet): RecyclerView.LayoutParams {
        val layoutParams = super.generateLayoutParams(c, attrs)
        val itemSize = calculateItemSize()
        layoutParams.width = itemSize
        layoutParams.height = itemSize
        return layoutParams
    }

    private fun updateSpanCount() {
        val availableWidth = width - paddingRight - paddingLeft
        val availableHeight = height - paddingTop - paddingBottom
        val spanCount = calculateSpanCount(availableWidth, availableHeight)
        if (spanCount == 0) {
            setSpanCount(1)
        } else {
            setSpanCount(spanCount)
        }
    }

    private fun calculateSpanCount(availableWidth: Int, availableHeight: Int): Int {
        val itemCount = itemCount
        val minSpanCount = 1 // Minimum span count
        val maxSpanCount = (availableWidth / minItemSize).coerceAtMost(itemCount) // Maximum span count

        var bestSpanCount = maxSpanCount
        var bestItemSize = 0
        val spacing = 20
        for (spanCount in minSpanCount..maxSpanCount)  {
            val itemSize = calculateItemSizeWithSpan(spanCount)
            if (itemSize in minItemSize..maxItemSize) {
                val rowCount = availableHeight / itemSize
                val availableWidthAfterSpacing = availableWidth - ((spanCount -1) * spacing)
                val totalItemWidth = spanCount * itemSize
                if (totalItemWidth <= availableWidthAfterSpacing) {
                    if (itemCount <= (rowCount * spanCount)) {
                        bestSpanCount = spanCount
                        bestItemSize = itemSize
                        break
                    }
                }
            }
        }
        return bestSpanCount
    }

    private fun calculateItemSizeWithSpan(spanCount: Int): Int {
        val availableWidth = width - paddingRight - paddingLeft
        val spacing = 20
        val totalSpacing = spacing * (spanCount - 1)
        val totalItemWidth = availableWidth - totalSpacing
        return totalItemWidth / spanCount
    }

    private fun calculateItemSize(): Int {
        val availableWidth = width - paddingRight - paddingLeft
        val spanCount = spanCount
        val spacing = 20
        val totalSpacing = spacing * (spanCount - 1)
        val totalItemWidth = availableWidth - totalSpacing
        return totalItemWidth / spanCount
    }
}

This is calculating the sizes correctly. However, I am facing an issue. In the first load, there are fewer items, say 8, so it displays an item of size 540 X 540.

Now when I switch to a different category, it has 40 items, so the best possible size is calculated as 240. In this case, the item is not drawn correctly.

Any idea why this is not drawn correctly? Is this a model view reuse issue?

英文:

I have en epoxy recycler view, that can display an item in the maximum possible size and also want to display the maximum number of items by adjusting span count and item size. For that, I am using this layout manager,

class GridAutoFitLayoutManager(
context: Context,
private val minItemSize: Int,
private val maxItemSize: Int
) : GridLayoutManager(context, 1) {
override fun onLayoutChildren(recycler: RecyclerView.Recycler, state: RecyclerView.State) {
updateSpanCount()
super.onLayoutChildren(recycler, state)
}
override fun generateDefaultLayoutParams(): RecyclerView.LayoutParams {
val layoutParams = super.generateDefaultLayoutParams()
val itemSize = calculateItemSize()
layoutParams.width = itemSize
layoutParams.height = itemSize
return layoutParams
}
override fun generateLayoutParams(c: Context, attrs: android.util.AttributeSet): RecyclerView.LayoutParams {
val layoutParams = super.generateLayoutParams(c, attrs)
val itemSize = calculateItemSize()
layoutParams.width = itemSize
layoutParams.height = itemSize
return layoutParams
}
private fun updateSpanCount() {
val availableWidth = width - paddingRight - paddingLeft
val availableHeight = height - paddingTop - paddingBottom
val spanCount = calculateSpanCount(availableWidth, availableHeight)
if (spanCount == 0) {
setSpanCount(1)
} else {
setSpanCount(spanCount)
}
}
private fun calculateSpanCount(availableWidth: Int, availableHeight: Int): Int {
val itemCount = itemCount
val minSpanCount = 1 // Minimum span count
val maxSpanCount = (availableWidth / minItemSize).coerceAtMost(itemCount) // Maximum span count
var bestSpanCount = maxSpanCount
var bestItemSize = 0
val spacing = 20
for (spanCount in minSpanCount..maxSpanCount)  {
val itemSize = calculateItemSizeWithSpan(spanCount)
if (itemSize in minItemSize..maxItemSize) {
val rowCount = availableHeight / itemSize
val availableWidthAfterSpacing = availableWidth - ((spanCount -1) * spacing)
val totalItemWidth = spanCount * itemSize
//&amp;&amp; (rowCount * spanCount) &lt;= itemCount
if (totalItemWidth &lt;= availableWidthAfterSpacing) {
if (itemCount &lt;= (rowCount * spanCount)) {
bestSpanCount = spanCount
bestItemSize = itemSize
break
}
}
}
}
return bestSpanCount
}
private fun calculateItemSizeWithSpan(spanCount: Int): Int {
val availableWidth = width - paddingRight - paddingLeft
val spacing = 20
val totalSpacing = spacing * (spanCount - 1)
val totalItemWidth = availableWidth - totalSpacing
return totalItemWidth / spanCount
}
private fun calculateItemSize(): Int {
val availableWidth = width - paddingRight - paddingLeft
val spanCount = spanCount
val spacing = 20
val totalSpacing = spacing * (spanCount - 1)
val totalItemWidth = availableWidth - totalSpacing
return totalItemWidth / spanCount
}
}

This is calculating the sizes correctly. however, I am facing an issue. In the first load, there are fewer items say 8, so it displays an item of size 540 X 540.

动态更改 RecyclerView 的跨度计数和 ModelView 大小

Now when I switch to a different category, it is having 40 items, so the best possible size is calculated as 240. In this case, the item is not drawn correctly.

动态更改 RecyclerView 的跨度计数和 ModelView 大小

Any idea why this is not drawn correctly? Is this a moel view reuse issue ?

答案1

得分: 1

我建议添加一个 checkLayoutParams 方法的覆盖实现:

override fun checkLayoutParams(lp : RecyclerView.LayoutParams) : Boolean {
   return lp.width == calculateItemSize()
}

这将在重用视图持有者的布局参数大小不合适时丢弃它们,并创建有效的参数来替换它们。

英文:

I suggest adding an override of checkLayoutParams method:

override fun checkLayoutParams(lp : RecyclerView.LayoutParams) : Boolean {
   return lp.width == calculateItemSize()
}

This will discard layout params of reused viewholders when their size is not okay and create valid params to replace them.

答案2

得分: 0

是这个一个模型视图重用的问题吗?

是的,epoxy模型将被重用,如果需要的话,你应该手动更改大小。
示例:

@ModelProp
fun setWidth(width: Int) {
  view.updateLayoutParams { 
    this.width = width
  }
}

但在你的情况下,我建议你可以查看:https://github.com/airbnb/epoxy/wiki/Grid-Support

然后根据每个特定情况进行覆盖。

英文:

> Is this a moel view reuse issue ?

Yes, the epoxy model will be reuse and you should manual change the size if you need.
Example:

@ModelProp
fun setWidth(width: Int) {
  view.updateLayoutParams { 
    this.width = with
}

But in your case, I suggest you can check: https://github.com/airbnb/epoxy/wiki/Grid-Support

And do an override base on each specific case.

huangapple
  • 本文由 发表于 2023年7月6日 17:58:32
  • 转载请务必保留本文链接:https://go.coder-hub.com/76627639.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定