Android 在不更改滚动位置的情况下动态更新Recyclerview项的布局
情景: 我有一个卡片列表,基本上是一个RecyclerView,Recycler视图中只有两个项目。我还扩展了RecyclerView.LayoutManager()并重写了LayoutManager类的onLayoutChildren()方法。 最初,第二张卡在底部,当我向上滑动时,第二张卡滚动到顶部,就在第一张卡的稍下方 单击第二张卡(列表中的第二项)中的“生成条形码”按钮,我将隐藏该按钮并显示替换buton的条形码图像。更新卡布局时,将自动调用onLayoutChildren()方法。此方法将被重写,以便在启动“回收器”视图时在特定位置显示卡。这将导致布局管理器重新绘制子视图。因此,第二张卡将滚动回初始位置 预期行为:当我们尝试更新第二张卡布局时,第二张卡不应向下滚动 StackCardLayoutManager.ktAndroid 在不更改滚动位置的情况下动态更新Recyclerview项的布局,android,android-layout,android-recyclerview,layout-manager,Android,Android Layout,Android Recyclerview,Layout Manager,情景: 我有一个卡片列表,基本上是一个RecyclerView,Recycler视图中只有两个项目。我还扩展了RecyclerView.LayoutManager()并重写了LayoutManager类的onLayoutChildren()方法。 最初,第二张卡在底部,当我向上滑动时,第二张卡滚动到顶部,就在第一张卡的稍下方 单击第二张卡(列表中的第二项)中的“生成条形码”按钮,我将隐藏该按钮并显示替换buton的条形码图像。更新卡布局时,将自动调用onLayoutChildren()方法。此方
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)
}
}
类StackCardLayoutManager(
私有val maxItemCount:Int
):RecyclerView.LayoutManager(){
private val addedChildren:列表
get()=(0直到childCount).map{getChildAt(it)?:抛出NullPointerException()}
private var firstTime:Boolean=true
初始化{
Log.d(标记“StackCardLayoutManager.init()”)
}
重写fun generateDefaultLayoutParams():RecyclerView.LayoutParams=
RecyclerView.LayoutParams(RecyclerView.LayoutParams.WRAP_内容,RecyclerView.LayoutParams.MATCH_父项)
override fun isAutoMeasureEnabled():Boolean=true
儿童在线娱乐(
回收商:回收商视图,回收商,
状态:RecyclerView.state
) {
Log.d(TAG_K,“StackCardLayoutManager.onLayoutChildren(itemcount:${state.itemcount})addedChildren大小:${addedChildren.Size}firstTime:$firstTime”)
第一次=错误
如果(state.itemCount==0){
返回
}
如果(state.itemCount>maxItemCount){
抛出RuntimeException(“不能设置超过$maxItemCount的项”)
}
拆卸和报废附件(回收器)
for(0中的i,直到state.itemCount){
Log.d(标签K,“StackCardLayoutManager.onLayoutChildren($i)-layoutDecorated”)
val view=recycler.getViewForPosition(i)
measureChild(视图,0,0)
添加视图(视图)
val layoutParams=view.layoutParams作为RecyclerView.layoutParams
val left=layoutParams.margintart
Log.d(标记K,“StackCardLayoutManager.onLayoutChildren()左:$left”)
val top=(view.measuredHeight*i*1.15).toInt()
Log.d(标记K,“StackCardLayoutManager.onLayoutChildren()top:$top”)
val right=view.measuredWidth+layoutParams.margined
Log.d(标记K,“StackCardLayoutManager.onLayoutChildren()右:$right”)
val底部=顶部+视图。测量高度
Log.d(标记K,“StackCardLayoutManager.onLayoutChildren()底部:$bottom”)
布局装饰(视图、左侧、顶部、右侧、底部)
view.setTag(InitializedPosition.TOP.key,TOP)
}
}
override fun canscrollvertical():Boolean=true
无情地覆盖乐趣(
狄:Int,
回收商:回收商视图,回收商,
状态:RecyclerView.state
):Int=dy.也{deltaY->
Log.d(“stackcardlayout”,“ScrollVerticalyBy:$deltaY”)
if(childCount==0){
Log.d(“stackcardlayout”,“ScrollVerticalyBy:子计数为0”)
return@also
}
变量deltaY1=0
addedChildren.ForAchineDexed{索引,视图->
val initializedTop=view.getTag(InitializedPosition.TOP.key)为Int
val layoutParams=view.layoutParams作为RecyclerView.layoutParams
val left=layoutParams.margintart
如果(三角洲<0){
deltaY1=-500
}否则{
deltaY1=500
}
val top=最小值(最大值((view.top-deltaY1),索引*dpToPx(70)),初始化为top)
val right=view.measuredWidth+layoutParams.margined
val底部=顶部+视图。测量高度
布局装饰(视图、左侧、顶部、右侧、底部)
}
}
私有枚举类InitializedPosition(val键:Int){
TOP(R.integer.TOP)
}
}
我试着用谷歌搜索这个解决方案,并在上发现了类似的问题,但该解决方案对androidx Recyclerview无效,但仅对android v7 Recycyler视图支持库有效。我看到了答案。建议您根据
eatRequestLayout()
方法来实现自定义视图requestLayout
方法
// RecyclerView requestlayout
@Override
public void requestLayout() {
if (!mEatRequestLayout) {
super.requestLayout();
} else {
mLayoutRequestEaten = true;
}
}
//RecyclerView.eatRequestLayout
void eatRequestLayout() {
if (!mEatRequestLayout) {
mEatRequestLayout = true;
mLayoutRequestEaten = false;
}
}
由于文档所述的requestLayout
,视图将根据它重新绘制
当某些更改使此视图的布局无效时调用此函数。这将计划视图树的布局过程。
在自定义视图requestLayout
中,您可以根据自己的情况设置不同的标志以防止重新绘制