RecyclerView that does not recycle
RecyclerView and header(s), one of the most commonly seen interface in mobile world!
Scenario:
- User interface with header(s) and a RecyclerView
Design:
At a first glance, this looks like a pretty simple layout, a simple scroll view nested with LinearLayout will do the tricks!
1 |
|
Result :
it looks not smooth at all, weird scrolling behaviour
In order to fix this, we can identify the issue it was the scrolling event that were acting weird, so let’s get to the solutions,
In Android, how people resolve scrolling conflicts mainly with this 2 methods:
- Old fashioned view touch event intercepting handling
- Implement NestedScrollingChild & NestedScrollingParent
Solutions 1: Put all of them in a NestedScrollView
Such a no brainer and it worked like a charm, however…
Drawback:
- RecyclerView lost ability to recycle itself because it is all laid out in NestedScrollView
Why:
- First thing that we need to know is how a view is showing on screen, it will go through our 3 important step which is Measure, Layout and Draw.
- For our NestedScrollView, it will need to measure its content height before laid out to the UI, so it will perform a measure on all of its children in order to get height of them.
- When it comes to RecyclerView, it will be go through measure layout and draw in order to get the measured height and width for the parent
- thus, all of the item in recyclerview will be create in one shot and no recycler mechanism will be apply on
- this is a big performance impact since the RecyclerView Recycling mechanism being break.
Extended why:
- NestedScrollView pass
MeasureSpec.UNSPECIFIED
to all of it’s child view to let them laid out the full height of it, - When RecyclerView received
MeasureSpec.UNSPECIFIED
from parent view and itself in XML is declare withwrap_content
ormatch_parent
, it will laid out all of it children directly with all the item height plus padding, thus the whole adapter view is inflated all at once
Underlying Mechanism:
It is using the NestedScrollingChild and NestedScrollingParent handling underlying in the framework
- NestedScrollView is a NestedScrolling Parent
- RecyclerView already implemented with NestedScrollingChild
How we can avoid this:
- Give Recyclerview a fixed height, so that the parent view can get the measuredheight that is declared
Solutions 2: Use Recyclerview multi item adapter to fixes
Item type to the rescue!
Just declare your header as one of the item type and use it anywhere with the power of recycling!
Solutions 3: Use CoordinatorLayout with custom child behaviour
This solution would need some exploration with CoordinatorLayout.
CoordinatorLayout is one of the NestedScrollingParent as well, thus, by overiding the behaviour onTouchIntercept, onFling, onNestedScroll, Developer can define which view should taking the control of the touch event and act respectively