RecyclerView that does not recycle

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
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
<?xml version="1.0" encoding="utf-8"?>
<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">

<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">

<TextView
android:layout_width="match_parent"
android:layout_height="200dp"
android:layout_gravity="center"
android:gravity="center"
android:textColor="@color/white"
android:background="@color/purple_500"
android:textSize="100sp"
android:text="Header" />

<androidx.recyclerview.widget.RecyclerView
android:id="@+id/rv"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
</LinearLayout>
</ScrollView>

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:

  1. Old fashioned view touch event intercepting handling
  2. 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 with wrap_content or match_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

Comments