李世德的博客

我有故事,你有酒吗?

0%

Android:解决 RecyclerView 的 item 复用引起的布局错乱问题

问题描述

列表 item 中有个 View 是在某些情况下显示或隐藏的,在 onBindViewHolder 绑定的时候会根据数据对其处理,View.VISIBLEView.GONE 都有写,看似没有问题。但是快速上下滑动的时候会发生 item 显示错乱:那个需要显示的控件会只显示一半,未完整撑开 item。item 的根布局是 ConstraintLayout

解决

思路 1

RecyclerView 复用了前面的控件,如果使用了 View.GONE 把控件隐藏了,当复用的时候却找不到了,所以导致有些 item 不能正常显示。于是,在 bind 开始的时候,先将可能需要根据需要改变状态的控件做一下初始化设置:View.VISIBLE

一般地,这么设置后应该能解决问题了。

然而,上面的思路并未解决我遇到的问题,还是一样的存在这个问题。

思路 2

怀疑可能 item 布局有问题,反复检查了 ConstraintLayout 的控件约束关系,感觉都是正确的。就算存在那个会隐藏的控件的显示与否问题,但它并不影响到依赖它的控件(ConstraintLayout特性之一,某个控件隐藏会被解析为一个点,margin 变为 0,依赖它的控件位置不会错乱)。差点想要放弃了,改用传统的布局 LinearLayout 来进行布局了。改的过程中,发现用下面的方案解决了这个问题。

ConstraintLayout 外层套了一个 FrameLayout,同样的手顺,快速上下滑动列表,不再复现之前的问题了,item 是期待的某些显示某些隐藏,高度自动完整撑开到完全显示的高度。

我想,这是解决了。根布局是约束布局的情况下,高度未被改变,复用了隐藏某控件的情况下的高度。具体原理有待研究,但是在约束布局外层嵌套一个传统布局,可以做到高度改变的目的。

联想到的另一个可能问题

某些需要有单选或多选的列表,在滑动后,也有可能出现状态丢失的问题。以前的开发中遇到过此需求,解决方法是在实体类中增加一个 isCheck 变量,根据 isCheck 的值处理选中状态。