Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
78 changes: 78 additions & 0 deletions src/Controls/tests/TestCases.HostApp/Issues/Issue28064.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
namespace Maui.Controls.Sample.Issues;

[Issue(IssueTracker.Github, 28064, "TapGestureRecognizer on ScrollView background does not fire on Android", PlatformAffected.Android)]
public class Issue28064 : ContentPage
{
public Issue28064()
{
var statusLabel = new Label
{
Text = "Tap the brown background",
AutomationId = "StatusLabel"
};

var childStatusLabel = new Label
{
Text = "Tap a child label",
AutomationId = "ChildStatusLabel"
};

var child1 = new Label
{
BackgroundColor = Colors.LightPink,
WidthRequest = 100,
HeightRequest = 80,
Text = "Child1",
AutomationId = "Child1Label"
};
var childTapRecognizer = new TapGestureRecognizer();
childTapRecognizer.Tapped += (s, e) =>
{
childStatusLabel.Text = "Child Tapped";
};
child1.GestureRecognizers.Add(childTapRecognizer);

// ScrollView with Brown background — the area NOT covered by children is the tap target
var scrollView = new ScrollView
{
HeightRequest = 80,
BackgroundColor = Colors.Brown,
Orientation = ScrollOrientation.Horizontal,
AutomationId = "TheScrollView",
Content = new HorizontalStackLayout
{
Children =
{
child1,
new Label
{
BackgroundColor = Colors.LightGreen,
WidthRequest = 100,
HeightRequest = 80,
Text = "Child2",
AutomationId = "Child2Label"
}
}
}
};

var tapRecognizer = new TapGestureRecognizer();
tapRecognizer.Tapped += (s, e) =>
{
statusLabel.Text = "ScrollView Tapped";
};
scrollView.GestureRecognizers.Add(tapRecognizer);

Content = new VerticalStackLayout
{
Spacing = 10,
Padding = 10,
Children =
{
statusLabel,
childStatusLabel,
scrollView
}
};
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
using NUnit.Framework;
using UITest.Appium;
using UITest.Core;

namespace Microsoft.Maui.TestCases.Tests.Issues;

public class Issue28064 : _IssuesUITest
{
public Issue28064(TestDevice device) : base(device)
{
}

public override string Issue => "TapGestureRecognizer on ScrollView background does not fire on Android";

[Test]
[Category(UITestCategories.ScrollView)]
public void ScrollViewBackgroundTapGestureShouldFire()
{
App.WaitForElement("StatusLabel");
App.Tap("TheScrollView");
Comment thread
Shalini-Ashokan marked this conversation as resolved.
Comment thread
Shalini-Ashokan marked this conversation as resolved.
var labelText = App.WaitForElement("StatusLabel").GetText();
Assert.That(labelText, Is.EqualTo("ScrollView Tapped"));
}

[Test]
[Category(UITestCategories.ScrollView)]
public void ScrollViewChildTapGestureShouldFire()
{
App.WaitForElement("ChildStatusLabel");
App.Tap("Child1Label");
var labelText = App.WaitForElement("ChildStatusLabel").GetText();
Assert.That(labelText, Is.EqualTo("Child Tapped"));
}
}
15 changes: 15 additions & 0 deletions src/Core/src/Platform/Android/MauiScrollView.cs
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,15 @@ public class MauiScrollView : NestedScrollView, IScrollBarView, NestedScrollView
internal bool ShouldSkipOnTouch;
internal int HorizontalScrollOffset => _hScrollView?.ScrollX ?? 0;

// Stores the parent touch listener so horizontal ScrollView taps can be forwarded to it directly.
internal IOnTouchListener? _touchListener;

public override void SetOnTouchListener(IOnTouchListener? touchListener)
{
_touchListener = touchListener;
base.SetOnTouchListener(touchListener);
}

public MauiScrollView(Context context) : base(context)
{
_context = context;
Expand Down Expand Up @@ -556,6 +565,12 @@ public override bool OnTouchEvent(MotionEvent? ev)
// This allows ScrollView to work properly inside containers like DrawerLayout (Shell Flyout)
ScrollViewExtensions.HandleTouchEvent(ev, Parent);

// OnTouchEvent is only called when no child has claimed the touch event, which mirrors
// exactly when a vertical ScrollView's touch listener fires. We invoke the parent's
// stored touch listener here so TapGestureRecognizers on a horizontal/both ScrollView
// fire correctly.
_parentScrollView._touchListener?.OnTouch(_parentScrollView, ev);

// If the touch is caught by the horizontal scrollview, forward it to the parent
_parentScrollView.ShouldSkipOnTouch = true;
_parentScrollView.OnTouchEvent(ev);
Expand Down
1 change: 1 addition & 0 deletions src/Core/src/PublicAPI/net-android/PublicAPI.Unshipped.txt
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ override Microsoft.Maui.PlatformDrawable.ThresholdType.get -> System.Type!
*REMOVED*override Microsoft.Maui.Graphics.MauiDrawable.OnDraw(Android.Graphics.Drawables.Shapes.Shape? shape, Android.Graphics.Canvas? canvas, Android.Graphics.Paint? paint) -> void
override Microsoft.Maui.Handlers.LabelHandler.GetDesiredSize(double widthConstraint, double heightConstraint) -> Microsoft.Maui.Graphics.Size
override Microsoft.Maui.Platform.MauiScrollView.OnVisibilityChanged(Android.Views.View! changedView, Android.Views.ViewStates visibility) -> void
override Microsoft.Maui.Platform.MauiScrollView.SetOnTouchListener(Android.Views.View.IOnTouchListener? touchListener) -> void
override Microsoft.Maui.Platform.ContentViewGroup.HasOverlappingRendering.get -> bool
override Microsoft.Maui.Platform.LayoutViewGroup.HasOverlappingRendering.get -> bool
override Microsoft.Maui.Platform.WrapperView.HasOverlappingRendering.get -> bool
Expand Down
Loading