Mobile Development 19 min read

Understanding Android NestedScrolling Mechanism – Fundamentals, Core Flow, and Implementation

This article explains the Android NestedScrolling mechanism, compares it with the traditional view event dispatch system, details the core scrolling flow, presents the key NestedScrollingParent and NestedScrollingChild interfaces with code examples, and walks through a practical demo and source‑level analysis.

Sohu Tech Products
Sohu Tech Products
Sohu Tech Products
Understanding Android NestedScrolling Mechanism – Fundamentals, Core Flow, and Implementation

Android NestedScrolling has become a common interaction effect for complex scrolling transformations in modern Android development. When a sophisticated slide effect is needed, developers often rely on the NestedScrolling mechanism to coordinate scrolling between parent and child views.

Traditional view event dispatch involves three main methods: dispatchTouchEvent for event distribution, onInterceptTouchEvent for interception (available only in ViewGroup ), and onTouchEvent for handling. The flow is top‑down, allowing only one view to react to a touch event.

NestedScrolling mechanism introduces a bidirectional flow where a child view first processes the touch event, converts it into a NestedScrolling event (dx, dy), and then dispatches it upward. The parent can consume part of the scroll in onNestedPreScroll and the remaining part in onNestedScroll , enabling seamless hand‑off between views.

Core flow (simplified):

The child calls startNestedScroll to initiate nested scrolling.

Before moving, the child invokes dispatchNestedPreScroll , allowing the parent’s onNestedPreScroll to consume part of the distance.

The child then scrolls the unconsumed portion.

After scrolling, the child calls dispatchNestedScroll , giving the parent a chance to handle any leftover distance via onNestedScroll .

When the gesture ends, the child calls stopNestedScroll , and the parent’s onStopNestedScroll performs cleanup.

Key interfaces

NestedScrollingParent – defines callbacks such as onStartNestedScroll , onNestedPreScroll , onNestedScroll , onNestedFling , etc.

NestedScrollingChild – provides methods like setNestedScrollingEnabled , startNestedScroll , dispatchNestedPreScroll , dispatchNestedScroll , dispatchNestedFling , etc.

Implementation example – Parent

public class MyNestedParent extends LinearLayout implements NestedScrollingParent {
    private MyNestedChild mNestedScrollChild;
    private NestedScrollingParentHelper mNestedScrollingParentHelper;
    private int mImgHeight;

    public MyNestedParent(Context context) { super(context); }
    public MyNestedParent(Context context, AttributeSet attrs) {
        super(context, attrs);
        mNestedScrollingParentHelper = new NestedScrollingParentHelper(this);
    }

    @Override
    public boolean onStartNestedScroll(@NonNull View child, @NonNull View target, int axes) {
        return target instanceof MyNestedChild && (axes & ViewCompat.SCROLL_AXIS_HORIZONTAL) != 0;
    }

    @Override
    public void onNestedPreScroll(@NonNull View target, int dx, int dy, @NonNull int[] consumed) {
        if (showImg(dy) || hideImg(dy)) {
            scrollBy(0, -dy);
            consumed[1] = dy;
        }
    }

    @Override
    public void onNestedScroll(@NonNull View target, int dxConsumed, int dyConsumed, int dxUnconsumed, int dyUnconsumed) {
        // optional handling of remaining scroll
    }

    @Override
    public void onStopNestedScroll(@NonNull View target) {
        mNestedScrollingParentHelper.onStopNestedScroll(target);
    }
}

Implementation example – Child

public class MyNestedChild extends LinearLayout implements NestedScrollingChild {
    private NestedScrollingChildHelper mNestedScrollingChildHelper;
    private final int[] offset = new int[2];
    private final int[] consumed = new int[2];
    private int lastY;

    private NestedScrollingChildHelper getScrollingChildHelper() {
        if (mNestedScrollingChildHelper == null) {
            mNestedScrollingChildHelper = new NestedScrollingChildHelper(this);
            mNestedScrollingChildHelper.setNestedScrollingEnabled(true);
        }
        return mNestedScrollingChildHelper;
    }

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        switch (event.getAction()) {
            case MotionEvent.ACTION_DOWN:
                lastY = (int) event.getRawY();
                break;
            case MotionEvent.ACTION_MOVE:
                int y = (int) event.getRawY();
                int dy = y - lastY;
                lastY = y;
                if (startNestedScroll(ViewCompat.SCROLL_AXIS_HORIZONTAL) &&
                    dispatchNestedPreScroll(0, dy, consumed, offset)) {
                    int remain = dy - consumed[1];
                    if (remain != 0) scrollBy(0, -remain);
                } else {
                    scrollBy(0, -dy);
                }
                break;
        }
        return true;
    }

    @Override public void setNestedScrollingEnabled(boolean enabled) { getScrollingChildHelper().setNestedScrollingEnabled(enabled); }
    @Override public boolean startNestedScroll(int axes) { return getScrollingChildHelper().startNestedScroll(axes); }
    @Override public void stopNestedScroll() { getScrollingChildHelper().stopNestedScroll(); }
    @Override public boolean dispatchNestedPreScroll(int dx, int dy, int[] consumed, int[] offset) {
        return getScrollingChildHelper().dispatchNestedPreScroll(dx, dy, consumed, offset);
    }
    @Override public boolean dispatchNestedScroll(int dxConsumed, int dyConsumed, int dxUnconsumed, int dyUnconsumed, int[] offsetInWindow) {
        return getScrollingChildHelper().dispatchNestedScroll(dxConsumed, dyConsumed, dxUnconsumed, dyUnconsumed, offsetInWindow);
    }
    // other interface methods omitted for brevity
}

The demo logs illustrate the exact sequence of method calls during a typical scroll gesture, confirming the interaction between child and parent:

MyNestedChild: onTouchEvent: ACTION_DOWN → startNestedScroll
MyNestedParent: onStartNestedScroll → onNestedScrollAccepted
MyNestedChild: ACTION_MOVE → dispatchNestedPreScroll → onNestedPreScroll (parent)
MyNestedChild: dispatchNestedScroll → onNestedScroll (parent)
MyNestedChild: stopNestedScroll → onStopNestedScroll (parent)

A simple XML layout shows a MyNestedParent containing an ImageView header and a RecyclerView child, demonstrating how the parent can scroll the header while the child scrolls its list.

<?xml version="1.0" encoding="utf-8"?>
<com.stayli.nested.view.MyNestedParent
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical">
    <ImageView
        android:id="@+id/cat_header"
        android:layout_width="match_parent"
        android:layout_height="200dp"
        android:src="@drawable/cat" />
    <android.support.v7.widget.RecyclerView
        android:id="@+id/rv_nested"
        android:layout_width="match_parent"
        android:layout_height="match_parent" />
</com.stayli.nested.view.MyNestedParent>

The article also outlines the full touch‑event lifecycle in RecyclerView , showing where startNestedScroll , dispatchNestedPreScroll , dispatchNestedScroll , and stopNestedScroll are invoked, and how the parent’s onNestedPreScroll and onNestedScroll methods consume or pass on the remaining distance.

Finally, nested fling handling mirrors the scroll flow: the child calls dispatchNestedPreFling and dispatchNestedFling , giving the parent a chance to react before and after the fling.

Overall, the article provides a comprehensive theoretical overview, method‑by‑method analysis, and practical code snippets to help Android developers master the NestedScrolling mechanism.

Mobile DevelopmentUIandroidRecyclerViewTouchEventCustom ViewNestedScrolling
Sohu Tech Products
Written by

Sohu Tech Products

A knowledge-sharing platform for Sohu's technology products. As a leading Chinese internet brand with media, video, search, and gaming services and over 700 million users, Sohu continuously drives tech innovation and practice. We’ll share practical insights and tech news here.

0 followers
Reader feedback

How this landed with the community

login Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.