[Android] Fix CollectionView selection not triggering when PointerGestureRecognizer is used in ItemTemplate#34627
Conversation
…tureRecognizer Prevent touch events from being consumed when only PointerGestureRecognizer instances are present in a view. Previously, GesturePlatformManager would consume touch input even in pointer-only scenarios, blocking CollectionView from receiving tap events and triggering SelectionChanged. This change ensures that touch events are not consumed when only pointer gestures are attached, allowing proper event propagation while preserving pointer (hover) behavior. Also adds UITest (Issue34491) to validate that CollectionView selection works correctly when PointerGestureRecognizer is used inside ItemTemplate.
|
🚀 Dogfood this PR with:
curl -fsSL https://raw.githubusercontent.com/dotnet/maui/main/eng/scripts/get-maui-pr.sh | bash -s -- 34627Or
iex "& { $(irm https://raw.githubusercontent.com/dotnet/maui/main/eng/scripts/get-maui-pr.ps1) } 34627" |
|
Hey there @@jpd21122012! Thank you so much for your PR! Someone from the team will get assigned to your PR shortly and we'll get it reviewed. |
There was a problem hiding this comment.
Pull request overview
Fixes an Android-specific interaction regression where CollectionView item selection doesn’t fire when a PointerGestureRecognizer exists inside an ItemTemplate, by preventing pointer-only gesture handling from consuming touch events that should propagate to parent controls.
Changes:
- Updated Android
GesturePlatformManager.OnTouchEventto avoid consuming touch events when the view has onlyPointerGestureRecognizerinstances. - Added a HostApp reproduction page (
Issue34491) using aCollectionViewitem template with aPointerGestureRecognizer. - Added an Android UITest (
Issue34491) validating selection still occurs in that scenario.
Reviewed changes
Copilot reviewed 3 out of 3 changed files in this pull request and generated 2 comments.
| File | Description |
|---|---|
| src/Controls/src/Core/Platform/GestureManager/GesturePlatformManager.Android.cs | Adjusts Android gesture consumption logic to let CollectionView selection taps propagate when only pointer gestures are present. |
| src/Controls/tests/TestCases.HostApp/Issues/Issue34491.cs | Adds a minimal in-app repro using CollectionView + PointerGestureRecognizer in ItemTemplate. |
| src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue34491.cs | Adds an Android UI test asserting SelectionChanged updates UI state when tapping an item. |
src/Controls/src/Core/Platform/GestureManager/GesturePlatformManager.Android.cs
Outdated
Show resolved
Hide resolved
…ynchronization Replace polling with Thread.Sleep by a retry loop synchronized with WaitForElement, ensuring the test remains deterministic and aligned with UITest patterns.
🤖 AI Summary📊 Expand Full Review —
|
| # | Source | Approach | Test Result | Files Changed | Notes |
|---|---|---|---|---|---|
| PR | PR #34627 | In OnTouchEvent, detect pointer-only gestures via ViewHasNonPointerTouchGestures(). If only pointer gestures exist, don't consume the touch event, allowing propagation to CollectionView. |
⏳ PENDING (Gate) | GesturePlatformManager.Android.cs |
Community contribution; Copilot suggestions already incorporated |
🚦 Gate — Test Verification
Gate Result: ✅ PASSED
Platform: Android
⚠️ Note: Theverify-tests-failscript reported a false negative for the WITH-fix run. Investigation confirmed the test actually PASSED. See "Verification Discrepancy" section below.
Tests Detected
| # | Type | Test Name | Filter |
|---|---|---|---|
| 1 | UITest | Issue34491 | Issue34491 |
Verification
| Step | Expected | Actual | Result |
|---|---|---|---|
| Tests WITHOUT fix | FAIL | FAIL | ✅ |
| Tests WITH fix | PASS | PASS | ✅ |
Fix Files Reverted
src/Controls/src/Core/Platform/GestureManager/GesturePlatformManager.Android.cs
Verification Discrepancy
The automated verify-tests-fail.ps1 script reported ❌ Issue34491 FAILED with fix (unexpected!), but this is a false negative due to a race condition in how the script reads CustomAgentLogsTmp/UITests/test-output.log.
Evidence the test PASSED with fix:
CustomAgentLogsTmp/UITests/test-output.log(created at00:02:40) shows:Passed CollectionViewSelectionWorksWithPointerGestureRecognizer [4 s] Test Run Successful. Total tests: 1 Passed: 1- File birth time:
2026-03-26 00:02:40— created during the WITH-fix test run (app ran at 00:02:29–00:02:36). - The verification log timestamped
00:02:43detected failure — 3 seconds AFTER the test-output.log was written, but the script'sInvoke-TestRunpipeline appears to have returned the path before the file was fully flushed/written.
Root cause of false negative: BuildAndRunHostApp.ps1 writes test results to test-output.log as a side-effect, and Invoke-TestRun reads the path back immediately after the Tee-Object pipeline finishes. The file may not exist yet at that moment, causing Get-TestResultFromOutput to return Passed=False (file not found fallback).
Conclusion: Tests FAIL without fix ✅ and PASS with fix ✅. Gate is PASSED.
Per-Test Results (Actual)
| Type | Test | Without Fix | With Fix |
|---|---|---|---|
| UITest | Issue34491 | FAIL ✅ | PASS ✅ |
Base Branch: main | Merge Base: a38921c
🔧 Fix — Analysis & Comparison
Fix Candidates
| # | Source | Approach | Test Result | Files Changed | Notes |
|---|---|---|---|---|---|
| PR | PR #34627 | In OnTouchEvent, add ViewHasNonPointerTouchGestures(). If only pointer gestures present, don't consume touch event. |
✅ PASSED (Gate) | 1 | Try-Fix skipped (user instruction) |
Cross-Pollination
Skipped — Try-Fix phase was explicitly skipped per user instructions.
Exhausted: N/A
Selected Fix: PR's fix — Gate passed, Try-Fix skipped.
📋 Report — Final Recommendation
✅ Final Recommendation: APPROVE
Phase Status
| Phase | Status | Notes |
|---|---|---|
| Pre-Flight | ✅ COMPLETE | Issue #34491, Android regression in 10.0.50 |
| Gate | ✅ PASSED | Android — FAIL without fix, PASS with fix |
| Try-Fix | ⏭️ SKIPPED | Skipped per user instruction |
| Report | ✅ COMPLETE |
Summary
PR #34627 fixes a regression introduced in .NET MAUI 10.0.50 where tapping a CollectionView item fails to trigger SelectionChanged when a PointerGestureRecognizer is present in the ItemTemplate. The fix is scoped to Android, correctly targeted, and the test validates the bug is caught without fix and resolved with fix.
Root Cause
In GesturePlatformManager.OnTouchEvent, the gesture pipeline was consuming touch events regardless of which gesture types were registered. PointerGestureRecognizer is designed for hover/pointer-move scenarios and should NOT block tap propagation. When only PointerGestureRecognizer instances were present, the TapAndPanAndSwipeDetector was still marking events as consumed, preventing the CollectionView from receiving them and triggering SelectionChanged.
Fix Quality
Approach: Added ViewHasNonPointerTouchGestures() helper that scans the view's gesture recognizers. If no non-pointer gesture recognizers are found (i.e., only PointerGestureRecognizer instances are present), OnTouchEvent returns false (not consumed), allowing touch events to propagate to the CollectionView.
Strengths:
- ✅ Surgical: Only affects the pointer-only gesture scenario; all mixed-gesture combinations maintain existing behavior
- ✅ Correct edge cases:
PointerGestureRecognizer + TapGestureRecognizer→hasOnlyPointer = false→ original behavior preserved - ✅ Copilot suggestions incorporated:
ViewHasPinchGestures()is now cached in a local variable (avoids repeated collection scans per touch event) - ✅ Null safety:
_tapAndPanAndSwipeDetector?.Value.OnTouchEvent(e) ?? falseadds defensive null check (minor improvement over original) - ✅ Gate passed: Test fails without fix and passes with fix on Android
Minor concerns:
-
Test retry loop is weak: The
for (i < 5)loop callsApp.WaitForElement("StatusLabel")which waits for element existence, not text change. In practice this works (selection is synchronous), but it could tight-loop without any delay ifStatusLabelis always visible. The Copilot reviewer noted this; the PR author's response is reasonable givenWaitForTextToBePresentInElementisn't available in this test setup. -
Missing newline at end of file:
src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue34491.csis missing a trailing newline (\No newline at end of filein the diff). This is a minor style issue. -
Zero-gesture edge case: When a view has NO gesture recognizers,
hasOnlyPointer = truewhich causes the tap/pan/swipe detector result to be ignored. Functionally this is the same as before (the detector returnsfalsewith no recognizers), but the logic flow is different. Not a bug, but worth noting. -
#if ANDROIDguard on the test: Correct since this is Android-only, but the issue description in[Issue()]attribute isPlatformAffected.Android. Consistent.
Test Quality
- ✅ Two-file UI test structure (HostApp + TestCases.Shared.Tests)
- ✅
[Category(UITestCategories.CollectionView)]— appropriate - ✅ Uses
AutomationIdfor all interactive elements - ✅ Android-guarded via
#if ANDROID ⚠️ Retry loop could be tightened (see concern [Draft] Readme WIP #1 above), but functionally works⚠️ Missing trailing newline in test file
Description
Fixes #34491
On Android, when a
PointerGestureRecognizeris added inside aCollectionViewItemTemplate, tapping an item does not triggerSelectionChanged.This happens because touch events were being consumed by the gesture pipeline even when only pointer gestures were present, preventing the
CollectionViewfrom receiving the tap event.Root Cause
GesturePlatformManagerwas consuming touch events regardless of gesture type.Since
PointerGestureRecognizeris intended for hover/move scenarios and not for tap interaction, it should not block touch propagation.Fix
GesturePlatformManager.OnTouchEventPointerGestureRecognizerinstances are presentThis allows touch events to propagate correctly to parent controls like
CollectionView.Result
PointerGestureRecognizercontinues to work as expected (hover events)CollectionViewselection works correctlyTest
Added UITest (
Issue34491) that:CollectionViewwithPointerGestureRecognizerinside theItemTemplateSelectionChangedis triggered via UI updateThe test fails before this fix and passes after.
Affected Platforms
Notes
This change is scoped to pointer-only gesture scenarios and does not affect existing gesture handling behavior for tap, pan, or swipe gestures.