【发布时间】:2021-09-18 08:51:03
【问题描述】:
场景: 我有一个卡片列表,基本上是一个 RecyclerView,Recycler 视图中只有两个项目。我也在扩展 RecyclerView.LayoutManager() 并覆盖 LayoutManager 类的 onLayoutChildren() 方法。 最初第二张卡片在底部,当我向上滑动时,第二张卡片滚动到顶部,就在第一张卡片下方一点点。
单击第二张卡片(列表中的第二项)中的“生成条形码”按钮时,我隐藏该按钮并显示替换该按钮的条形码图像。更新卡片布局时,会自动调用 onLayoutChildren() 方法。此方法被覆盖以在回收器视图启动时在特定位置显示卡片。这会导致布局管理器重绘子视图。因此,第二张卡片正在滚动回初始位置。
预期行为:当我们尝试更新第二张卡片布局时,第二张卡片不应向下滚动。
StackCardLayoutManager.kt
class StackCardLayoutManager(
private val maxItemCount: Int
) : RecyclerView.LayoutManager() {
private val addedChildren: List<View>
get() = (0 until childCount).map { getChildAt(it) ?: throw NullPointerException() }
private var firstTime: Boolean = true
init {
Log.d(TAG_K, "StackCardLayoutManager.init()")
}
override fun generateDefaultLayoutParams(): RecyclerView.LayoutParams =
RecyclerView.LayoutParams(RecyclerView.LayoutParams.WRAP_CONTENT, RecyclerView.LayoutParams.MATCH_PARENT)
override fun isAutoMeasureEnabled(): Boolean = true
override fun onLayoutChildren(
recycler: RecyclerView.Recycler,
state: RecyclerView.State
) {
Log.d(TAG_K, "StackCardLayoutManager.onLayoutChildren(itemcount : ${state.itemCount}) addedChildren Size : ${addedChildren.size} firstTime : $firstTime")
firstTime = false
if (state.itemCount == 0) {
return
}
if (state.itemCount > maxItemCount) {
throw RuntimeException("Can not set more Item than $maxItemCount")
}
detachAndScrapAttachedViews(recycler)
for (i in 0 until state.itemCount) {
Log.d(TAG_K, "StackCardLayoutManager.onLayoutChildren($i) - layoutDecorated")
val view = recycler.getViewForPosition(i)
measureChild(view, 0, 0)
addView(view)
val layoutParams = view.layoutParams as RecyclerView.LayoutParams
val left = layoutParams.marginStart
Log.d(TAG_K, "StackCardLayoutManager.onLayoutChildren() left : $left")
val top = (view.measuredHeight * i * 1.15).toInt()
Log.d(TAG_K, "StackCardLayoutManager.onLayoutChildren() top : $top")
val right = view.measuredWidth + layoutParams.marginEnd
Log.d(TAG_K, "StackCardLayoutManager.onLayoutChildren() right : $right")
val bottom = top + view.measuredHeight
Log.d(TAG_K, "StackCardLayoutManager.onLayoutChildren() bottom : $bottom")
layoutDecorated(view, left, top, right, bottom)
view.setTag(InitializedPosition.TOP.key, top)
}
}
override fun canScrollVertically(): Boolean = true
override fun scrollVerticallyBy(
dy: Int,
recycler: RecyclerView.Recycler,
state: RecyclerView.State
): Int = dy.also { deltaY ->
Log.d("stackcardlayout", "scrollVerticallyBy: $deltaY")
if (childCount == 0) {
Log.d("stackcardlayout", "scrollVerticallyBy: child count is 0")
return@also
}
var deltaY1 = 0
addedChildren.forEachIndexed { index, view ->
val initializedTop = view.getTag(InitializedPosition.TOP.key) as Int
val layoutParams = view.layoutParams as RecyclerView.LayoutParams
val left = layoutParams.marginStart
if(deltaY < 0){
deltaY1 = -500
}else {
deltaY1 = 500
}
val top = min(max((view.top - deltaY1), index * dpToPx(70)), initializedTop)
val right = view.measuredWidth + layoutParams.marginEnd
val bottom = top + view.measuredHeight
layoutDecorated(view, left, top, right, bottom)
}
}
private enum class InitializedPosition(val key: Int) {
TOP(R.integer.top)
}
}
我尝试在这个解决方案周围搜索并在Stackoverflow 上发现了类似的问题,但是该解决方案对 androidx Recyclerview 无效,但仅对 android v7 Recycyler 视图支持库有效。
【问题讨论】:
标签: android android-layout android-recyclerview