前言
在这篇文章中,我们将学习如何优化 Android 中的 RecyclerView 性能。通过这些优化,我们可以使 RecyclerView 滚动流畅。
当我们在我们的 Android 应用程序中实现 RecyclerView 时,有时我们会遇到这样的问题: RecyclerView 项目滚动不顺畅。这会导致糟糕的用户体验,因为我们的 Android 应用程序似乎很滞后。
让我们看看我们可以做些什么来提高 RecyclerView 的性能,从而获得平滑的滚动。
RecyclerView 优化技巧
让我们开始吧
使用图像加载库
由于垃圾回收(GC)运行在主线程上,UI无响应的原因之一是内存的不断分配和释放,导致GC运行非常频繁。通过使用位图池的概念,我们可以避免它。
最好的部分是像 Glide、Fresco 这样的 Image-Loading 库使用了这个位图池概念。因此,请始终使用图像加载库。
将所有与图像相关的任务委托给这些库。
设置图像宽度和高度
如果我们的图像宽度和高度是动态的(不是固定的),并且我们imageUrl从服务器获取。
如果我们事先没有设置正确的图像宽度和高度,UI 会在加载(下载图像)和设置图像到 ImageView 的过渡过程中闪烁(实际上是在下载完成时使其可见)。
所以,我们应该要求我们的后端开发者发送图像大小或纵横比,据此,我们可以计算出所需的 ImageView 的宽度和高度。
然后,我们将只能先设置宽度和高度。因此没有闪烁。问题解决了!
少做 onBindViewHolder 方法
我们的 onBindViewHolder 方法应该做的工作很少。我们应该检查我们的 onBindViewHolder 方法并对其进行优化。通过这样做,我们可以看到 RecyclerView 的改进。
使用通知项目 RecyclerView API
每当我们有删除、更新、添加项目的用例时,使用通知项目 API。
<pre class="public-DraftStyleDefault-pre" data-offset-key="7inds-0-0" style="margin: 1.4em 0px; padding: 0.88889em; font-size: 0.9em; word-break: normal; overflow-wrap: normal; white-space: pre; overflow: auto; background: rgb(246, 246, 246); border-radius: 4px; color: rgb(18, 18, 18); font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration-thickness: initial; text-decoration-style: initial; text-decoration-color: initial;">
<pre class="Editable-styled" data-block="true" data-editor="b345q" data-offset-key="7inds-0-0" style="margin: 0px; padding: 0px; font-size: 0.9em; word-break: normal; overflow-wrap: normal; white-space: pre; overflow: initial; background: rgb(246, 246, 246); border-radius: 0px;">
adapter.notifyItemRemoved(position)
adapter.notifyItemChanged(position)
adapter.notifyItemInserted(position)
adapter.notifyItemRangeInserted(start, end)
</pre>
</pre>
另外,检查DiffUtil。
万一我们被迫使用notifyDataSetChanged()基于我们的用例,我们可以尝试setHasStableIds(true).
<pre class="public-DraftStyleDefault-pre" data-offset-key="rvf3-0-0" style="margin: 1.4em 0px; padding: 0.88889em; font-size: 0.9em; word-break: normal; overflow-wrap: normal; white-space: pre; overflow: auto; background: rgb(246, 246, 246); border-radius: 4px; color: rgb(18, 18, 18); font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration-thickness: initial; text-decoration-style: initial; text-decoration-color: initial;">
<pre class="Editable-styled" data-block="true" data-editor="b345q" data-offset-key="rvf3-0-0" style="margin: 0px; padding: 0px; font-size: 0.9em; word-break: normal; overflow-wrap: normal; white-space: pre; overflow: initial; background: rgb(246, 246, 246); border-radius: 0px;">
adapter.setHasStableIds(true)
</pre>
</pre>
它指示数据集中的每个项目是否可以用 Long 类型的唯一标识符表示。
即使我们调用notifyDataSetChanged(),它也不必处理整个适配器的完全重新排序,因为它可以找到某个位置的项目是否与以前相同并且做更少的工作。
避免嵌套视图
如果可能,我们应该避免嵌套视图,并尽可能创建平面视图。嵌套会降低 RecyclerView 的性能。平面视图提高了性能。
使用 setHasFixedSize
如果所有项目的高度相等,我们应该使用这种方法。添加以下内容并检查性能。
<pre class="public-DraftStyleDefault-pre" data-offset-key="c13hk-0-0" style="margin: 1.4em 0px; padding: 0.88889em; font-size: 0.9em; word-break: normal; overflow-wrap: normal; white-space: pre; overflow: auto; background: rgb(246, 246, 246); border-radius: 4px; color: rgb(18, 18, 18); font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration-thickness: initial; text-decoration-style: initial; text-decoration-color: initial;">
<pre class="Editable-styled" data-block="true" data-editor="b345q" data-offset-key="c13hk-0-0" style="margin: 0px; padding: 0px; font-size: 0.9em; word-break: normal; overflow-wrap: normal; white-space: pre; overflow: initial; background: rgb(246, 246, 246); border-radius: 0px;">
recyclerView.setHasFixedSize(true)
</pre>
</pre>
使用 setRecycledViewPool 优化嵌套 RecyclerView
众所周知,RecyclerView 尽可能地遵循“Reuse View Using Pool”的原则。
检查 RecyclerView 如何在内部工作?
如果我们有嵌套 RecyclerView 的用例。
<pre class="public-DraftStyleDefault-pre" data-offset-key="8lih4-0-0" style="margin: 1.4em 0px; padding: 0.88889em; font-size: 0.9em; word-break: normal; overflow-wrap: normal; white-space: pre; overflow: auto; background: rgb(246, 246, 246); border-radius: 4px; color: rgb(18, 18, 18); font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration-thickness: initial; text-decoration-style: initial; text-decoration-color: initial;">
<pre class="Editable-styled" data-block="true" data-editor="b345q" data-offset-key="8lih4-0-0" style="margin: 0px; padding: 0px; font-size: 0.9em; word-break: normal; overflow-wrap: normal; white-space: pre; overflow: initial; background: rgb(246, 246, 246); border-radius: 0px;">
- OuterRecyclerView
- InnerRecyclerViewOne
- InnerRecyclerViewTwo
</pre>
</pre>
但默认情况下,优化适用于特定的 RecyclerView,因为特定的 RecyclerView 有自己的视图池。
池不会在具有相同视图类型的两个 RecyclerView 之间共享。
所以,我们可以做的是,我们可以创建一个 ViewPool 并将其传递给所有内部的 RecyclerViews,以便它像下面这样共享:
class OuterRecyclerViewAdapter() : RecyclerView.Adapter<RecyclerView.ViewHolder>() {
// code removed for brevity
private val viewPool = RecyclerView.RecycledViewPool()
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder {
// code removed for brevity
viewHolderOne.innerRecyclerViewOne.setRecycledViewPool(viewPool)
// code removed for brevity
viewHolderTwo.innerRecyclerViewTwo.setRecycledViewPool(viewPool)
}
// code removed for brevity
}
这将提高滚动性能,因为它将开始重用共享 ViewPool 中的视图。
使用 setItemViewCacheSize
我们可以通过设置 ItemView Cache Size来进行实验。
recyclerView.setItemViewCacheSize(cacheSize)
根据官方文档:它设置在将它们添加到可能共享的回收视图池之前要保留的屏幕外视图的数量。屏幕外视图缓存保持对附加适配器中的更改的感知,允许 LayoutManager 重用这些未修改的视图,而无需返回到适配器重新绑定它们。
这意味着当我们滚动 RecyclerView 以使视图几乎完全脱离屏幕时,RecyclerView 将保留它,以便我们可以将其滚动回视图而无需onBindViewHolder()再次调用。
一般来说,我们不会改变 View Cache 的大小,而是尝试一下,如果它适合你,就实现它。
这些是我们可以做的事情来提高 RecyclerView 的性能。现在就是这样。
作者:Amit Shekhar
链接:https://blog.mindorks.com/recyclerview-optimization
网友评论