其他
Recyclerview竟能如此丝滑,这14个优化策略不容错过...
The following article is from Android补给站 Author Rouse
引言
在Android开发中,RecyclerView是一种常用的列表控件,用于展示大量数据。然而,随着数据量的增加,RecyclerView的性能可能会受到影响,导致卡顿、内存泄漏等问题。本文将介绍一些优化技巧,帮助大家提升RecyclerView的性能,使其在各种情况下都能保持流畅。
布局优化: 优化 RecyclerView 的布局结构,减少嵌套层级,提高布局效率。 减少绘制: 尽可能减少视图的绘制次数,避免过度绘制带来的性能消耗。 滑动优化: 在滑动过程中,尽可能的减少耗时操作,避免影响滑动效果。 预加载: 预加载即将显示的视图,提高展示性能。 内存优化: 减少内存的消耗,合理释放内存,避免内存泄漏。
下面针对这些分别给出具体的优化策略。
减少布局嵌套
<!-- item_layout.xml -->
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="wrap_content">
<TextView
android:id="@+id/textView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"/>
<!-- 其他视图组件 -->
</androidx.constraintlayout.widget.ConstraintLayout>
使用merge标签来合并布局
<!-- 使用merge标签合并布局 -->
<merge xmlns:android="http://schemas.android.com/apk/res/android">
<ImageView
android:id="@+id/imageView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="@drawable/image" />
<TextView
android:id="@+id/textView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Text" />
</merge>
启用setHasFixedSize
val recyclerView = findViewById<RecyclerView>(R.id.recyclerView)
recyclerView.setHasFixedSize(true)
需要注意的是,使用 setHasFixedSize(true)适用于所有Item高度固定且不会发生变化的情况。如果Item高度不固定或者会发生变化,应该避免使用该方法,否则可能导致布局显示异常。
使用DiffUtil进行数据更新
class MyDiffCallback(private val oldList: List<String>, private val newList: List<String>) : DiffUtil.Callback() {
override fun getOldListSize(): Int {
return oldList.size
}
override fun getNewListSize(): Int {
return newList.size
}
override fun areItemsTheSame(oldItemPosition: Int, newItemPosition: Int): Boolean {
return oldList[oldItemPosition] == newList[newItemPosition]
}
override fun areContentsTheSame(oldItemPosition: Int, newItemPosition: Int): Boolean {
return oldList[oldItemPosition] == newList[newItemPosition]
}
}
// 在Adapter中应用DiffUtil
val diffResult = DiffUtil.calculateDiff(MyDiffCallback(oldList, newList))
diffResult.dispatchUpdatesTo(this)
限制列表项的数量
// 仅加载可见范围内的数据
recyclerView.layoutManager?.setInitialPrefetchItemCount(10)
在onCreateViewHolder中进行必要的初始化操作
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder {
val view = LayoutInflater.from(parent.context).inflate(R.layout.item_layout, parent, false)
val viewHolder = ViewHolder(view)
// 进行必要的初始化操作
return viewHolder
}
滑动停止加载操作
val recyclerView = findViewById<RecyclerView>(R.id.recyclerView)
val layoutManager = LinearLayoutManager(this)
recyclerView.layoutManager = layoutManager
val adapter = MyAdapter(dataList)
recyclerView.adapter = adapter
recyclerView.addOnScrollListener(object : RecyclerView.OnScrollListener() {
override fun onScrollStateChanged(recyclerView: RecyclerView, newState: Int) {
super.onScrollStateChanged(recyclerView, newState)
// 判断滚动状态是否为停止滚动状态
if (newState == RecyclerView.SCROLL_STATE_IDLE) {
startLoaidng()
} else {
// 执行停止加载操作,例如停止图片加载等
stopLoading()
}
}
})
启动calculateExtraLayoutSpace
class CustomLayoutManager : LinearLayoutManager {
constructor(context: Context) : super(context)
constructor(context: Context, orientation: Int, reverseLayout: Boolean) : super(context, orientation, reverseLayout)
override fun calculateExtraLayoutSpace(state: RecyclerView.State, extraLayoutSpace: IntArray) {
super.calculateExtraLayoutSpace(state, extraLayoutSpace)
// 设置额外的布局空间,可以根据需要动态计算
extraLayoutSpace[0] = 200
extraLayoutSpace[1] = 200
}
}
重写collectAdjacentPrefetchPositions
class CustomLayoutManager : LinearLayoutManager {
constructor(context: Context) : super(context)
constructor(context: Context, orientation: Int, reverseLayout: Boolean) : super(context, orientation, reverseLayout)
override fun collectAdjacentPrefetchPositions(dx: Int, dy: Int, state: RecyclerView.State?, layoutPrefetchRegistry: LayoutPrefetchRegistry) {
super.collectAdjacentPrefetchPositions(dx, dy, state, layoutPrefetchRegistry)
// 根据滑动方向(dx, dy)收集相邻的预取位置
val anchorPos = findFirstVisibleItemPosition()
if (dy > 0) {
// 向下滑动,预取下面的Item数据
for (i in anchorPos + 1 until state?.itemCount ?: 0) {
layoutPrefetchRegistry.addPosition(i, 0)
}
} else {
// 向上滑动,预取上面的Item数据
for (i in anchorPos - 1 downTo 0) {
layoutPrefetchRegistry.addPosition(i, 0)
}
}
}
}
共用RecyclerViewPool
// 创建一个共享的RecycledViewPool
val recycledViewPool = RecyclerView.RecycledViewPool()
// 设置共享的RecycledViewPool给多个RecyclerView
recyclerView1.setRecycledViewPool(recycledViewPool)
recyclerView2.setRecycledViewPool(recycledViewPool)
使用Adapter.setHasStableIds(true)提高Item稳定性
adapter.setHasStableIds(true)
使用RecyclerView.setItemViewCacheSize(size)设置缓存大小
recyclerView.setItemViewCacheSize(20) // 设置缓存大小为20
共享事件
// 共用的监听器对象
val itemClickListener = View.OnClickListener { view ->
// 根据view的ID来执行不同的操作
when (view.id) {
R.id.button -> {
// 执行按钮点击操作
}
R.id.imageView -> {
// 执行图片点击操作
}
// 其他ID的处理...
}
}
// 在ViewHolder中为ItemView设置共用的监听器
inner class ViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
init {
// 为所有需要的ItemView设置共用的监听器
itemView.setOnClickListener(itemClickListener)
}
}
重写RecyclerView.onViewRecycled(holder)回收资源
override fun onViewRecycled(holder: ViewHolder) {
super.onViewRecycled(holder)
// 释放ViewHolder中的图片资源
holder.imageView.setImageDrawable(null)
// 移除ViewHolder中的监听器
holder.itemView.setOnClickListener(null)
}
最后推荐一下我做的网站,玩Android: wanandroid.com ,包含详尽的知识体系、好用的工具,还有本公众号文章合集,欢迎体验和收藏!
推荐阅读:
如何科学的进行Android包体积优化
Android项目开发模板推荐
扫一扫 关注我的公众号
如果你想要跟大家分享你的文章,欢迎投稿~
┏(^0^)┛明天见!