第一次滚动时在RecyclerView中滞后

我显示设备的联系人列表。在我的Android版本9的三星Galaxy S8中,当我第一次滚动recyclerView时,它并不平滑且有点滞后。但是随后它开始非常流畅地滚动。如果我使用“后退”按钮关闭该应用程序并再次启动该应用程序,它将再次平滑滚动,但是如果我从最近的历史记录中销毁了该应用程序的实例,然后再次启动该应用程序,则它并不平滑且仅在第一次滚动时出现了一些滞后。(我已经在Google Pixel2中进行了测试,当黄油含量较低时,我会感到与我解释的相同。)

这是Galaxy s8中该问题的录制屏幕:https ://drive.google.com/file/d/1szfF1oKEYZK3LIqQHC-MsapRxdJ85bFy/view ? usp = sharing

我尽可能优化了recyclerView适配器,看来问题与我的适配器无关。您可以在此处检查源代码:https : //github.com/Ali-Rezaei/Contacts

我有一个自定义的CoordinatorLayout.Behavior来隐藏/显示AppBarLayout以及bottomBar。我确信这与之无关,因为当没有CoordinatorLayout行为时,正如我上面解释的那样,它再次显示出滞后。

这是我的recyclerView项目布局:

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" 
    android:layout_height="wrap_content" 
    android:background="@android:color/white" 
    android:orientation="vertical">
    
    <include layout="@layout/contact_separator" />
    
    <include layout="@layout/contact_detail" />
    
    <LinearLayout
        android:id="@+id/subItem"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:orientation="vertical" />
    
    </LinearLayout>

contact_separator和contact_detail都是ConstaintLayouts。

这是我设置recyclerView的方法:

mAdapter = new ContactsAdapter();
binding.recyclerView.setHasFixedSize(true);
binding.recyclerView.setAdapter(mAdapter);

final Observer<Resource<List<Contact>>> contactsObserver = resource -> {
            if (resource instanceof Resource.Success) {
                mContacts = ((Resource.Success<List<Contact>>) resource).getData();
                mAdapter.setItems(mContacts, true);
            }
        };

这可能是什么原因?

马丁·马可西尼

没有运行项目就很难说,但是快速浏览(只需2-3分钟浏览您的适配器代码),就会发现一些“气味”。

  1. 您使用的是普通的RecyclerView.Adapter,应该/应该ListAdapter<T, K>与aDiffUtilCallback一起使用以避免notifyDataSetChanged()在执行操作使用setList(...)(相反,您只需要执行操作,submitList(...)然后让Adapter为您解决问题即可。

  2. 您的bind(...)方法相当复杂且漫长;有两个潜在的循环和视图参数更改(窗口小部件尺寸),这至少会导致一次度量传递(随后是布局传递),所有这些都会在您提交列表时发生在视图持有者中的每个项目上。创建应用程序(销毁应用程序后)时,必须再次计算所有这些内容,因此回收站视图的setFixedSize = true(为什么需要此功能,鉴于您的适配器,这不是真实的)。如果固定大小为true,那么将避免调用requestLayout(),但这意味着您要告诉适配器其大小是恒定的(这意味着适配器可以根据项数进行增长/收缩,但所有项的大小均相同)。这是一种优化,但可能会咬人,这很可能不是您想要的。

  3. flagItems是LinearLayout,您可以在运行时在bind方法内动态添加/删除视图到此线性布局。

  4. 您对subItem(另一个LinearLayout)执行相同的操作

  5. 此外,还有再次另一个回路(联系电话)在布局时可能再次修改的可能(约束集更改)。

所有这些(还有一些其他详细信息),但是现在就可以做...当涉及适配器时,它们是“红色标志”。

有趣的是,所有这一切都应该在更少的时间内发生,这需要Android渲染一帧(60毫秒?)。因此,您要求代码做很多工作并引发很多副作用,并期望它在60ms或更短的时间内完成。对于要绑定的每个EACH视图(取决于设备的屏幕尺寸),因此可以想象屏幕上可以容纳5个项目;RecyclerView会预先绑定一些内容以便在滚动时做好准备。

你能做什么?

我将从退后一步开始List<Contact> contacts可以更好地转换这些联系人以在RecyclerView上显示。关于显示什么/如何显示它的所有逻辑,应提前解决,并以最扁平/最简单的形式呈现给适配器(已经有其他工作要做)。type通过将数据分解为不同的类型,并使适配器简单地为每种视图类型绑定正确的viewHolder,可以利用ViewHolders的优势。

因此,List<Contact>您可以提供一个List<SomeOtherObject>更不适合viewHolder的,而只包含您以后需要重新获取原始数据Contact(或重建它)所需的数据,而不是一个。

然后,您可以从viewHolder#bind方法中简化并删除所有逻辑/决策,因为在确定要绑定的数据类型时,很多问题都可以解决。

我将从这里开始,因为您可以尝试优化所需的一切,但是您仍在要求适配器为您完成所有这些工作。

当您“不要终止应用程序”时,为什么不会发生这种情况?

那是因为(我估计)事物已被缓存并预先计算(它们已经花了第一时间),所以因为您拥有FixedSize = true,所以RecyclerView知道(已被告知)每个ViewHolder的大小都不会改变,因此无需重新计算。

简而言之,您并没有使适配器具有更高的性能,只是告诉他花那么多时间一次。;)

本文收集自互联网,转载请注明来源。

如有侵权,请联系 [email protected] 删除。

编辑于
0

我来说两句

0 条评论
登录 后参与评论

相关文章

UICollectionView第一次加载时明显滞后

recyclerView不会第一次平滑滚动

RecyclerView中onBindView上的setAlpha()在第一次显示时不起作用

为什么导航抽屉在第一次打开时滞后?

嵌套的RecyclerView在第一次滚动后在项目之间添加空间

在Safari中滚动时,元素消失--webkit-transform fix仅在第一次时有效

当我第一次向下滚动回收器视图时,变量值将在onBindViewHolder中重置

如何在第一次打开屏幕时滚动到 ScrollView 中的 image[x]?

iOS UITableViewCell内容在第一次滚动时移动

jQuery scrollTop第一次不滚动

检测用户是否仅第一次滚动

RecyclerView-第一次加载时Layout_weight被忽略

RecyclerView在第一次加载时不显示数据

在第一次滚动中,UILabel不会更改UITableViewCell中的高度

Recyclerview不会第一次加载(Firebase)

Recyclerview仅在第一次显示数据

在没有动画的情况下在 ViewPager2 中第一次滚动

UITableviewCell 中的 UICollectionView 第一次给出错误的 contentSizeHeight 但滚动后变为 true

第一次运行时,android list在recyclerview中获得0值

JQuery 动画在第一次运行时滞后

仅在第一次或刷新时加载Javascript

第一次发射时的RXJS

在第一次分页时选择状态

第一次加载时“$ 未定义”

在第一次出现字符时拆分

for循环仅在第一次迭代时执行

第一次出现“-”时拆分列

第一次安装时的getElementByID为空

SpeechRecognizer在第一次收听时引发onError