Building a Zhihu-Style Collapsing Header with Android's NestedScrolling Protocol
CoordinatorLayout abstracts away nested scrolling, but understanding the raw protocol is essential when its behavior doesn't match a design spec or when building custom containers that need to coordinate scroll consumption across multiple children. The V2/V3 protocol's conversion of fling into per-frame scroll events is the detail that makes inertial scrolling feel continuous in a collapsing header, and getting the consumed-pixel accounting wrong produces visible glitches.
Android's traditional event dispatch cannot hand off a scroll gesture from a parent to a child mid-gesture without lifting the finger, which breaks the continuous collapse-then-scroll behavior seen in apps like Zhihu. The NestedScrolling protocol solves this by letting the child View drive the coordination, asking the parent how much of each scroll delta it wants to consume before the child uses the remainder.
The implementation walks through a custom `MyCollapsingLayout` that implements `NestedScrollingParent3`. Measurement logic ensures the RecyclerView height accounts for the always-visible toolbar and sticky header so no blank space appears after the profile collapses. Layout uses `translationY` offsets rather than resizing to avoid repeated layout passes and jank. The core callbacks, `onNestedPreScroll` and `onNestedScroll`, consume vertical deltas to collapse or expand the header while returning exact consumed pixel values to prevent scroll misalignment.
Fling transitions work seamlessly because the V2/V3 protocol converts inertial scrolling into `TYPE_NON_TOUCH` scroll events dispatched through the same `onNestedPreScroll` path. The piece also traces the protocol's evolution from V1's broken fling handling through V3's precise post-scroll consumption feedback.
CoordinatorLayout's Behavior-based API hides the parent-child negotiation that NestedScrolling makes explicit, so developers who only know CoordinatorLayout often can't debug scroll glitches when a custom Behavior misreports consumed deltas.
The protocol's design inverts the traditional Android event model: instead of the parent intercepting from the child, the child volunteers scroll distance to the parent, which is why it can switch consumers mid-gesture without a finger lift.
Returning `false` from `onNestedPreFling` and `onNestedFling` while relying on the V2/V3 `TYPE_NON_TOUCH` dispatch is a deliberate design choice that eliminates an entire class of fling-continuity bugs present in V1 implementations.