Nested RecyclerView无法回收问题求助:三层嵌套列表内存异常
Hey there, let's tackle this head-on—nested RecyclerViews (especially with expandable layers) are tricky, and your issue of views never being recycled (plus onBindViewHolder stopping to trigger) is a classic symptom of misconfigured layout logic. Let's break down the root causes and actionable fixes:
Core Reasons This Happens
RecyclerView relies on correctly calculating the size of its child views to decide which ones to recycle. With a 3-level nested structure:
- The outer RecyclerView might be miscalculating the height of its child (the expandable inner RecyclerView), leading it to think all views are within the viewport (so no need to recycle).
- Fixed-size settings or unupdated layout notifications when expanding/collapsing can lock the RecyclerView into keeping all views loaded.
- Overly rigid layout parameters (like
match_parenton inner RecyclerViews) can break the measurement chain.
Step-by-Step Fixes
1. Disable Fixed Size for All RecyclerViews
By default, setHasFixedSize(true) tells RecyclerView its size won't change—but when you expand/collapse inner views, the size does change. This mismatch makes the outer RecyclerView skip recycling because it assumes all content fits the initial size.
Add this to every RecyclerView in your hierarchy:
recyclerView.setHasFixedSize(false)
2. Fix Layout Parameters for Inner RecyclerViews
Never use match_parent for the height of nested RecyclerViews. Instead, set it to wrap_content—but you need to ensure the inner RecyclerView's LayoutManager properly measures its content. For LinearLayoutManager, this usually works out of the box, but double-check your XML:
<androidx.recyclerview.widget.RecyclerView android:id="@+id/inner_expandable_rv" android:layout_width="match_parent" android:layout_height="wrap_content" />
3. Notify Outer RecyclerView When Inner Content Changes
When your expandable RecyclerView toggles its expanded state, the height of its parent (the outer RecyclerView's item) changes. If you don't notify the outer RecyclerView, it won't re-measure that item—so it keeps all views in memory.
Add a listener to your expandable RecyclerView and trigger a refresh:
innerExpandableRv.setOnExpandCollapseListener { isExpanded -> // Get the position of this inner RV's item in the outer RecyclerView val outerItemPosition = outerRv.getChildAdapterPosition(parentView) outerRv.notifyItemChanged(outerItemPosition) }
This forces the outer RecyclerView to re-calculate the item's height and trigger recycling as needed.
4. Flatten the Hierarchy (Long-Term Solution)
Nested RecyclerViews are often a band-aid. For complex 3-level structures, consider a single RecyclerView with multiple item types. You can handle expand/collapse logic by tracking the state of each parent item and inflating the appropriate child views.
This approach eliminates nested measurement issues entirely, as the single RecyclerView can manage recycling efficiently across all levels.
5. Custom LayoutManager (If Needed)
If wrap_content still doesn't work for your inner RecyclerView, you might need a custom LayoutManager that overrides onMeasure to calculate the exact height of the content. For example, a LinearLayoutManager variant that measures all children to get the total height:
class WrapContentLinearLayoutManager(context: Context) : LinearLayoutManager(context) { override fun onMeasure(recycler: RecyclerView.Recycler, state: RecyclerView.State, widthSpec: Int, heightSpec: Int) { val height = if (state.itemCount == 0) { super.onMeasure(recycler, state, widthSpec, heightSpec) 0 } else { val view = recycler.getViewForPosition(0) measureChild(view, widthSpec, heightSpec) val measuredHeight = view.measuredHeight * state.itemCount setMeasuredDimension(View.MeasureSpec.getSize(widthSpec), measuredHeight) measuredHeight } // Apply the measured height val newHeightSpec = View.MeasureSpec.makeMeasureSpec(height, View.MeasureSpec.EXACTLY) super.onMeasure(recycler, state, widthSpec, newHeightSpec) } }
Use this for your inner RecyclerViews to ensure accurate height measurement.
Final Checks
- Make sure you're not holding strong references to views in your Adapters (this can prevent GC from recycling them even if RecyclerView tries).
- Test scroll behavior after each fix—start with disabling fixed size and notifying outer RecyclerView, as those are the most impactful quick wins.
内容的提问来源于stack exchange,提问作者Stamatis Stiliats

