RecyclerView 原理与优化实战
从源码到实践的深度解析
目录
RecyclerView 架构
回收池机制
LayoutManager 布局
Adapter 原理
性能优化
常见问题解决
1. RecyclerView 架构 四大核心组件
flowchart TB
RecyclerView["RecyclerView"] --> Adapter["Adapter<br/>数据绑定"]
RecyclerView --> LayoutManager["LayoutManager<br/>测量 + 布局 + 滚动"]
RecyclerView --> Recycler["Recycler<br/>缓存 + 回收"]
Adapter --> ViewHolder["ViewHolder<br/>itemView + bind"]
LayoutManager --> ViewHolder
Recycler --> ViewHolder
创建 RecyclerView 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 <androidx.recyclerview.widget.RecyclerView android:id="@+id/recyclerView" android:layout_width="match_parent" android:layout_height="match_parent" /> val recyclerView = findViewById<RecyclerView>(R.id.recyclerView)recyclerView.layoutManager = LinearLayoutManager(this ) recyclerView.adapter = MyAdapter(items) recyclerView.addItemDecoration(DividerItemDecoration(this , LinearLayoutManager.VERTICAL)) recyclerView.itemAnimator = DefaultItemAnimator()
2. 回收池机制 RecyclerView 缓存层级
flowchart TB
Attached["第 1 层: mAttachedScrap<br/>屏幕内可见的 ViewHolder<br/>用于 data binding 和 position 复用"]
Cache["第 2 层: mCacheViews<br/>默认 2 个<br/>快速复用,不重新 bind"]
Pool["第 3 层: RecyclerViewPool<br/>多个 RecyclerView 共享<br/>按 ViewType 分类"]
Create["第 4 层: Adapter 创建新的 ViewHolder"]
Attached --> Cache --> Pool --> Create
Recycler 核心方法 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 fun getViewHolderForPosition (position: Int ) : ViewHolder { val scrapHolder = getScrapViewForPosition(position, TYPE_INVALID, true ) if (scrapHolder != null ) return scrapHolder val cachedHolder = getCachedViewForPosition(position) if (cachedHolder != null ) return cachedHolder val poolHolder = getRecycledViewPool().getRecycledView(viewType) if (poolHolder != null ) return poolHolder return createViewHolder(viewType) } fun recycleViewHolder (holder: ViewHolder ) { if (forceRecycle || holder.isRecyclable()) { recycledViewPool.putRecycledView(holder) } }
3. LayoutManager 布局 LinearLayoutManager 原理 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 override fun onLayoutChildren (recycler: Recycler , state: RecyclerView .State ) { detachAndScrapAttachedViews(recycler) fill(recycler, state, layoutState) recycleByLayoutState(recycler, layoutState) } private int fill(RecyclerView.Recycler recycler, LayoutState layoutState, RecyclerView.State state) { while ((layoutState.mLayoutDirection == LayoutState.LAYOUT_START && layoutState.hasMore(state)) || layoutState.mLayoutDirection == LayoutState.LAYOUT_END) { View view = recycler.getViewForPosition(layoutState.mCurrentPosition); addView(view); measureChildWithMargins(view, 0 , 0 ); layoutDecorated(view, left, top, right, bottom); layoutState.mCurrentPosition += layoutState.mItemDirection; } }
滚动实现 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 override fun scrollVerticallyBy (dy: Int , recycler: Recycler , state: RecyclerView .State ) : Int { if (dy > 0 ) { fill(recycler, state, layoutState) } offsetChildrenVertical(-dy) recyclerByLayoutState(recycler, state) return dy }
4. Adapter 原理 ViewHolder 绑定 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 class MyAdapter (private val items: List<Item>) : RecyclerView.Adapter<MyAdapter.ViewHolder>() { class ViewHolder (itemView: View) : RecyclerView.ViewHolder(itemView) { val textView: TextView = itemView.findViewById(R.id.text) } override fun onCreateViewHolder (parent: ViewGroup , viewType: Int ) : ViewHolder { val view = LayoutInflater.from(parent.context) .inflate(R.layout.item_layout, parent, false ) return ViewHolder(view) } override fun onBindViewHolder (holder: ViewHolder , position: Int ) { holder.textView.text = items[position].text } override fun getItemCount () = items.size } class MyAdapter : RecyclerView.Adapter <ViewHolder >() { private val oldItems = mutableListOf<Item>() private val newItems = mutableListOf<Item>() fun submitList (newList: List <Item >) { val diffCallback = DiffCallback(oldItems, newList) val diffResult = DiffUtil.calculateDiff(diffCallback) newItems.clear() newItems.addAll(newList) diffResult.dispatchUpdatesTo(this ) } } class DiffCallback ( private val oldList: List<Item>, private val newList: List<Item> ) : DiffUtil.Callback() { override fun getOldListSize () = oldList.size override fun getNewListSize () = newList.size override fun areItemsTheSame (oldPos: Int , newPos: Int ) : Boolean { return oldList[oldPos].id == newList[newPos].id } override fun areContentsTheSame (oldPos: Int , newPos: Int ) : Boolean { return oldList[oldPos] == newList[newPos] } }
5. 性能优化 RecyclerView 优化清单 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 recyclerView.setHasFixedSize(true ) class MyAdapter : RecyclerView.Adapter <ViewHolder >() { override fun getItemId (position: Int ) : Long = items[position].id } recyclerView.setItemId(getItemId = true ) val layoutManager = LinearLayoutManager(this ).apply { initialPrefetchItemCount = 4 } recyclerView.setItemViewCacheSize(20 ) recyclerView.itemAnimator = null
ViewHolder 优化 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 class BadViewHolder (itemView: View) : RecyclerView.ViewHolder(itemView) { fun bind (item: Item ) { itemView.findViewById<TextView>(R.id.text).text = item.text } } class GoodViewHolder (itemView: View) : RecyclerView.ViewHolder(itemView) { private val textView: TextView = itemView.findViewById(R.id.text) fun bind (item: Item ) { textView.text = item.text } }
数据层优化 1 2 3 4 5 6 7 8 9 10 11 12 adapter.notifyDataSetChanged() adapter.submitList(newList) notifyItemChanged(position) notifyItemRangeChanged(position, count) notifyItemRemoved(position) notifyItemInserted(position) notifyItemMoved(from, to)
6. 常见问题解决 解决嵌套 RecyclerView 滚动冲突 1 2 3 4 5 6 7 innerRecyclerView.isNestedScrollingEnabled = false recyclerView.layoutManager = object : LinearLayoutManager(context) { override fun canScrollVertically () = false }
ItemDecoration 分割线 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 class SimpleDividerDecoration : ItemDecoration () { private val divider: Drawable? = null override fun onDraw (c: Canvas , parent: RecyclerView , state: RecyclerView .State ) { val left = parent.paddingLeft val right = parent.width - parent.paddingRight for (i in 0 until parent.childCount - 1 ) { val child = parent.getChildAt(i) val params = child.layoutParams as RecyclerView.LayoutParams val top = child.bottom + params.bottomMargin val bottom = top + divider?.intrinsicHeight ?: 0 divider?.setBounds(left, top, right, bottom) divider?.draw(c) } } }
面试常问
问题
答案
RecyclerView 缓存层级?
Attached → Cache → Pool → create
notifyDataSetChanged vs DiffUtil?
DiffUtil 高效,局部更新
为什么滑动卡顿?
绑定太重、View 过多、GC
总结
flowchart TB
Core["RecyclerView 核心"] --> R1["Recycler: 缓存机制,决定复用效率"]
Core --> R2["Adapter: 数据绑定,DiffUtil 差量更新"]
Core --> R3["LayoutManager: 布局 + 滚动"]
Core --> R4["优化: 预取、固定大小、DiffUtil"]
相关文章 :