diff --git a/Xamarin.Forms.Controls.Issues/Xamarin.Forms.Controls.Issues.Shared/Bugzilla59863_0.cs b/Xamarin.Forms.Controls.Issues/Xamarin.Forms.Controls.Issues.Shared/Bugzilla59863_0.cs
new file mode 100644
index 000000000..3b607b17f
--- /dev/null
+++ b/Xamarin.Forms.Controls.Issues/Xamarin.Forms.Controls.Issues.Shared/Bugzilla59863_0.cs
@@ -0,0 +1,92 @@
+using Xamarin.Forms.CustomAttributes;
+using Xamarin.Forms.Internals;
+
+#if UITEST
+using Xamarin.UITest;
+using NUnit.Framework;
+using Xamarin.Forms.Core.UITests;
+#endif
+
+namespace Xamarin.Forms.Controls.Issues
+{
+#if UITEST
+ [Category(UITestCategories.Gestures)]
+#endif
+
+ [Preserve(AllMembers = true)]
+ [Issue(IssueTracker.Bugzilla, 59863, "TapGestureRecognizer extremely finicky", PlatformAffected.Android)]
+ public class Bugzilla59863_0 : TestContentPage
+ {
+ int _singleTaps;
+ const string SingleTapBoxId = "singleTapView";
+
+ const string Singles = "singles(s)";
+
+ protected override void Init()
+ {
+ var instructions = new Label
+ {
+ Text = "Tap the box below several times quickly. "
+ + "The number displayed below should match the number of times you tap the box."
+ };
+
+ var singleTapCounter = new Label {Text = $"{_singleTaps} {Singles}"};
+
+ var singleTapBox = new BoxView
+ {
+ WidthRequest = 100,
+ HeightRequest = 100,
+ BackgroundColor = Color.Bisque,
+ AutomationId = SingleTapBoxId
+ };
+
+ var singleTap = new TapGestureRecognizer
+ {
+ Command = new Command(() =>
+ {
+ _singleTaps = _singleTaps + 1;
+ singleTapCounter.Text = $"{_singleTaps} {Singles} on {SingleTapBoxId}";
+ })
+ };
+
+ singleTapBox.GestureRecognizers.Add(singleTap);
+
+ Content = new StackLayout
+ {
+ Margin = 40,
+ HorizontalOptions = LayoutOptions.Fill, VerticalOptions = LayoutOptions.Fill,
+ Children = { instructions, singleTapBox, singleTapCounter }
+ };
+ }
+
+#if UITEST
+ [Test]
+ public void TapsCountShouldMatch()
+ {
+ // Gonna add this test because we'd want to know if it _did_ start failing
+ // But it doesn't really help much with this issue; UI test can't tap fast enough to demonstrate the
+ // problem we're trying to solve
+
+ int tapsToTest = 5;
+
+ RunningApp.WaitForElement(SingleTapBoxId);
+
+ for (int n = 0; n < tapsToTest; n++)
+ {
+ RunningApp.Tap(SingleTapBoxId);
+ }
+
+ RunningApp.WaitForElement($"{tapsToTest} {Singles} on {SingleTapBoxId}");
+ }
+
+ [Test]
+ public void DoubleTapWithOnlySingleTapRecognizerShouldRegisterTwoTaps()
+ {
+ RunningApp.WaitForElement(SingleTapBoxId);
+ RunningApp.DoubleTap(SingleTapBoxId);
+
+ RunningApp.WaitForElement($"2 {Singles} on {SingleTapBoxId}");
+ }
+#endif
+ }
+}
\ No newline at end of file
diff --git a/Xamarin.Forms.Controls.Issues/Xamarin.Forms.Controls.Issues.Shared/Bugzilla59863_1.cs b/Xamarin.Forms.Controls.Issues/Xamarin.Forms.Controls.Issues.Shared/Bugzilla59863_1.cs
new file mode 100644
index 000000000..4eb4396a9
--- /dev/null
+++ b/Xamarin.Forms.Controls.Issues/Xamarin.Forms.Controls.Issues.Shared/Bugzilla59863_1.cs
@@ -0,0 +1,84 @@
+using Xamarin.Forms.CustomAttributes;
+using Xamarin.Forms.Internals;
+
+#if UITEST
+using Xamarin.UITest;
+using NUnit.Framework;
+using Xamarin.Forms.Core.UITests;
+#endif
+
+namespace Xamarin.Forms.Controls.Issues
+{
+#if UITEST
+ [Category(UITestCategories.Gestures)]
+#endif
+
+ [Preserve(AllMembers = true)]
+ [Issue(IssueTracker.Bugzilla, 59863, "TapGestureRecognizer extremely finicky", PlatformAffected.Android,
+ issueTestNumber:1)]
+ public class Bugzilla59863_1 : TestContentPage
+ {
+ int _doubleTaps;
+ const string DoubleTapBoxId = "doubleTapView";
+
+ const string Doubles = "double(s)";
+
+ protected override void Init()
+ {
+ var instructions = new Label
+ {
+ Text = "Tap the box below once. The counter should not increment. "
+ + "Double tap the box. The counter should increment."
+ };
+
+ var doubleTapCounter = new Label {Text = $"{_doubleTaps} {Doubles} on {DoubleTapBoxId}"};
+
+ var doubleTapBox = new BoxView
+ {
+ WidthRequest = 100,
+ HeightRequest = 100,
+ BackgroundColor = Color.Chocolate,
+ AutomationId = DoubleTapBoxId
+ };
+
+ var doubleTap = new TapGestureRecognizer
+ {
+ NumberOfTapsRequired = 2,
+ Command = new Command(() =>
+ {
+ _doubleTaps = _doubleTaps + 1;
+ doubleTapCounter.Text = $"{_doubleTaps} {Doubles} on {DoubleTapBoxId}";
+ })
+ };
+
+ doubleTapBox.GestureRecognizers.Add(doubleTap);
+
+ Content = new StackLayout
+ {
+ Margin = 40,
+ HorizontalOptions = LayoutOptions.Fill, VerticalOptions = LayoutOptions.Fill,
+ Children = { instructions, doubleTapBox, doubleTapCounter }
+ };
+ }
+
+#if UITEST
+ [Test]
+ public void SingleTapWithOnlyDoubleTapRecognizerShouldRegisterNothing()
+ {
+ RunningApp.WaitForElement(DoubleTapBoxId);
+ RunningApp.Tap(DoubleTapBoxId);
+
+ RunningApp.WaitForElement($"0 {Doubles} on {DoubleTapBoxId}");
+ }
+
+ [Test]
+ public void DoubleTapWithOnlyDoubleTapRecognizerShouldRegisterOneDoubleTap()
+ {
+ RunningApp.WaitForElement(DoubleTapBoxId);
+ RunningApp.DoubleTap(DoubleTapBoxId);
+
+ RunningApp.WaitForElement($"1 {Doubles} on {DoubleTapBoxId}");
+ }
+#endif
+ }
+}
\ No newline at end of file
diff --git a/Xamarin.Forms.Controls.Issues/Xamarin.Forms.Controls.Issues.Shared/Bugzilla59863_2.cs b/Xamarin.Forms.Controls.Issues/Xamarin.Forms.Controls.Issues.Shared/Bugzilla59863_2.cs
new file mode 100644
index 000000000..2513935cd
--- /dev/null
+++ b/Xamarin.Forms.Controls.Issues/Xamarin.Forms.Controls.Issues.Shared/Bugzilla59863_2.cs
@@ -0,0 +1,99 @@
+using Xamarin.Forms.CustomAttributes;
+using Xamarin.Forms.Internals;
+
+#if UITEST
+using Xamarin.UITest;
+using NUnit.Framework;
+using Xamarin.Forms.Core.UITests;
+#endif
+
+namespace Xamarin.Forms.Controls.Issues
+{
+#if UITEST
+ [Category(UITestCategories.Gestures)]
+#endif
+
+ [Preserve(AllMembers = true)]
+ [Issue(IssueTracker.Bugzilla, 59863, "TapGestureRecognizer extremely finicky", PlatformAffected.Android,
+ issueTestNumber: 2)]
+ public class Bugzilla59863_2 : TestContentPage
+ {
+ int _mixedSingleTaps;
+ int _mixedDoubleTaps;
+ const string MixedTapBoxId = "mixedTapView";
+
+ const string Singles = "singles(s)";
+ const string Doubles = "double(s)";
+
+ protected override void Init()
+ {
+ var instructions = new Label
+ {
+ Text = "Tap the box below once. The single tap counter should increment. "
+ + "Double tap the box. The double tap counter should increment, "
+ + "but the single tap counter should not."
+ };
+
+ var mixedSingleTapCounter = new Label {Text = $"{_mixedSingleTaps} {Singles}"};
+ var mixedDoubleTapCounter = new Label {Text = $"{_mixedDoubleTaps} {Doubles}"};
+
+ var mixedTapBox = new BoxView
+ {
+ WidthRequest = 100,
+ HeightRequest = 100,
+ BackgroundColor = Color.Coral,
+ AutomationId = MixedTapBoxId
+ };
+
+ var mixedDoubleTap = new TapGestureRecognizer
+ {
+ NumberOfTapsRequired = 2,
+ Command = new Command(() =>
+ {
+ _mixedDoubleTaps = _mixedDoubleTaps + 1;
+ mixedDoubleTapCounter.Text = $"{_mixedDoubleTaps} {Doubles} on {MixedTapBoxId}";
+ })
+ };
+
+ var mixedSingleTap = new TapGestureRecognizer
+ {
+ NumberOfTapsRequired = 1,
+ Command = new Command(() =>
+ {
+ _mixedSingleTaps = _mixedSingleTaps + 1;
+ mixedSingleTapCounter.Text = $"{_mixedSingleTaps} {Singles} on {MixedTapBoxId}";
+ })
+ };
+
+ mixedTapBox.GestureRecognizers.Add(mixedDoubleTap);
+ mixedTapBox.GestureRecognizers.Add(mixedSingleTap);
+
+ Content = new StackLayout
+ {
+ Margin = 40,
+ HorizontalOptions = LayoutOptions.Fill, VerticalOptions = LayoutOptions.Fill,
+ Children = { instructions, mixedTapBox, mixedSingleTapCounter, mixedDoubleTapCounter }
+ };
+ }
+
+#if UITEST
+ [Test]
+ public void DoubleTapWithMixedRecognizersShouldRegisterDoubleTap()
+ {
+ RunningApp.WaitForElement(MixedTapBoxId);
+ RunningApp.DoubleTap(MixedTapBoxId);
+
+ RunningApp.WaitForElement($"1 {Doubles} on {MixedTapBoxId}");
+ }
+
+ [Test]
+ public void SingleTapWithMixedRecognizersShouldRegisterSingleTap()
+ {
+ RunningApp.WaitForElement(MixedTapBoxId);
+ RunningApp.Tap(MixedTapBoxId);
+
+ RunningApp.WaitForElement($"1 {Singles} on {MixedTapBoxId}");
+ }
+#endif
+ }
+}
\ No newline at end of file
diff --git a/Xamarin.Forms.Controls.Issues/Xamarin.Forms.Controls.Issues.Shared/Xamarin.Forms.Controls.Issues.Shared.projitems b/Xamarin.Forms.Controls.Issues/Xamarin.Forms.Controls.Issues.Shared/Xamarin.Forms.Controls.Issues.Shared.projitems
index bada851c9..5424e49da 100644
--- a/Xamarin.Forms.Controls.Issues/Xamarin.Forms.Controls.Issues.Shared/Xamarin.Forms.Controls.Issues.Shared.projitems
+++ b/Xamarin.Forms.Controls.Issues/Xamarin.Forms.Controls.Issues.Shared/Xamarin.Forms.Controls.Issues.Shared.projitems
@@ -222,6 +222,9 @@
+
+
+
diff --git a/Xamarin.Forms.Platform.Android/InnerGestureListener.cs b/Xamarin.Forms.Platform.Android/InnerGestureListener.cs
index 99c1d63d2..c529211d5 100644
--- a/Xamarin.Forms.Platform.Android/InnerGestureListener.cs
+++ b/Xamarin.Forms.Platform.Android/InnerGestureListener.cs
@@ -61,7 +61,20 @@ namespace Xamarin.Forms.Platform.Android
if (_disposed)
return false;
- return _tapDelegate(2);
+ if (HasDoubleTapHandler())
+ {
+ return _tapDelegate(2);
+ }
+
+ if (HasSingleTapHandler())
+ {
+ // If we're registering double taps and we don't actually have a double-tap handler,
+ // but we _do_ have a single-tap handler, then we're really just seeing two singles in a row
+ // Fire off the delegate for the second single-tap (OnSingleTapUp already did the first one)
+ return _tapDelegate(1);
+ }
+
+ return false;
}
bool GestureDetector.IOnDoubleTapListener.OnDoubleTapEvent(MotionEvent e)
@@ -134,7 +147,7 @@ namespace Xamarin.Forms.Platform.Android
if (!HasDoubleTapHandler())
{
- // We're not worried about double-tap, so OnSingleTap has already run the delegate
+ // We're not worried about double-tap, so OnSingleTapUp has already run the delegate
// there's nothing for us to do here
return false;
}
@@ -201,5 +214,12 @@ namespace Xamarin.Forms.Platform.Android
return false;
return _tapGestureRecognizers(2).Any();
}
+
+ bool HasSingleTapHandler()
+ {
+ if (_tapGestureRecognizers == null)
+ return false;
+ return _tapGestureRecognizers(1).Any();
+ }
}
}
\ No newline at end of file