Skip to content

[inflight regression] Android nested CollectionView can cancel child-owned horizontal gestures after Issue7814 parent handoff fix #35862

Description

@AdamEssenmacher

After the Issue7814 fix in PR #35656, Android MauiRecyclerView can steal a touch stream from descendant row content even after that content has claimed touch ownership with RequestDisallowInterceptTouchEvent(true).

This preserves the original Issue7814 behavior for perpendicular nested scrolling, but it violates Android’s child touch ownership contract in layouts where a vertical CollectionView is nested inside a horizontal scroll parent.

Affected Scenario
Android only.

A real-world shape is:

  • Horizontal parent, such as CarouselView
  • Vertical child CollectionView
  • Row content with a horizontal gesture, such as SwipeView or a custom native-backed gesture view
  • Row content calls RequestDisallowInterceptTouchEvent(true) after Down / Move

Repro Steps

  1. Create a horizontal parent CarouselView.
  2. Inside each carousel item, place a vertical CollectionView.
  3. Inside a row, place native-backed content that consumes touch events.
  4. On Down and Move, have the row content call Parent?.RequestDisallowInterceptTouchEvent(true).
  5. Drag horizontally across that row.

Actual Behavior
The row receives Down / Move, then receives Cancel.

The horizontal parent CarouselView advances position, meaning the parent stole the gesture after the child claimed it.

Expected Behavior
The row should receive the full stream ending in Up.

The parent CarouselView should not advance once descendant content has claimed the stream with RequestDisallowInterceptTouchEvent(true).

Impact
This can make horizontal row interactions inside nested Android CollectionView layouts feel broken. Taps and short gestures still work, but controls whose primary interaction requires dragging past touch slop can become effectively unusable in this layout. Examples include SwipeView, custom sliders, drag handles, and native-backed gesture controls inside rows.

Regression Source
This appears to be a follow-up regression from PR #35656, which added the parent handoff behavior for Issue7814. The handoff correctly enables perpendicular gestures to transfer from a nested RecyclerView to a scrollable parent, but it does not account for descendant content that has already disallowed interception.

Proposed Fix
Keep the Issue7814 parent handoff behavior, but make ParentScrollGestureDispatcher respect descendant touch ownership:

  • Track descendant RequestDisallowInterceptTouchEvent(true) during the undecided touch stream.
  • Before canceling/replaying the stream to the parent, let the current move dispatch once through the normal RecyclerView path so a late child claim can be observed.
  • If a descendant has claimed the stream, lock ownership to the normal RecyclerView / child path and do not forward to the parent.
  • Preserve existing own-axis behavior, including CarouselView.IsSwipeEnabled.

A fix branch exists with a focused HostApp repro and Appium test:
AdamEssenmacher:fix-issue7814-child-gesture

Test added:
TouchClaimingRowInsideVerticalCollectionViewNestedInHorizontalParentKeepsHorizontalGesture

Validation:
The regressed build reproduces Cancel + parent position change. The fixed build produces Up and keeps the parent position unchanged.

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions