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
- Create a horizontal parent
CarouselView.
- Inside each carousel item, place a vertical
CollectionView.
- Inside a row, place native-backed content that consumes touch events.
- On
Down and Move, have the row content call Parent?.RequestDisallowInterceptTouchEvent(true).
- 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.
After the Issue7814 fix in PR #35656, Android
MauiRecyclerViewcan steal a touch stream from descendant row content even after that content has claimed touch ownership withRequestDisallowInterceptTouchEvent(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
CollectionViewis nested inside a horizontal scroll parent.Affected Scenario
Android only.
A real-world shape is:
CarouselViewCollectionViewSwipeViewor a custom native-backed gesture viewRequestDisallowInterceptTouchEvent(true)afterDown/MoveRepro Steps
CarouselView.CollectionView.DownandMove, have the row content callParent?.RequestDisallowInterceptTouchEvent(true).Actual Behavior
The row receives
Down/Move, then receivesCancel.The horizontal parent
CarouselViewadvances 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
CarouselViewshould not advance once descendant content has claimed the stream withRequestDisallowInterceptTouchEvent(true).Impact
This can make horizontal row interactions inside nested Android
CollectionViewlayouts 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 includeSwipeView, 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
RecyclerViewto 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
ParentScrollGestureDispatcherrespect descendant touch ownership:RequestDisallowInterceptTouchEvent(true)during the undecided touch stream.RecyclerViewpath so a late child claim can be observed.RecyclerView/ child path and do not forward to the parent.CarouselView.IsSwipeEnabled.A fix branch exists with a focused HostApp repro and Appium test:
AdamEssenmacher:fix-issue7814-child-gestureTest added:
TouchClaimingRowInsideVerticalCollectionViewNestedInHorizontalParentKeepsHorizontalGestureValidation:
The regressed build reproduces
Cancel+ parent position change. The fixed build producesUpand keeps the parent position unchanged.