[Android] Handle rapid taps when only single-tap recognizer is present (#1188)

* Handle rapid tapping correctly

* Remove failed optimization attempt for 2-pointer gestures
This commit is contained in:
E.Z. Hart 2017-10-16 08:23:22 -06:00 коммит произвёл Rui Marinho
Родитель 40f574bc46
Коммит adc5ff1eb9
5 изменённых файлов: 303 добавлений и 2 удалений

Просмотреть файл

@ -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
}
}

Просмотреть файл

@ -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
}
}

Просмотреть файл

@ -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
}
}

Просмотреть файл

@ -218,6 +218,12 @@
<Compile Include="$(MSBuildThisFileDirectory)Bugzilla57910.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Bugzilla58406.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Bugzilla58833.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Bugzilla51427.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Bugzilla59248.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Bugzilla59580.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Bugzilla59863_0.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Bugzilla59863_1.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Bugzilla59863_2.cs" />
<Compile Include="$(MSBuildThisFileDirectory)ButtonBackgroundColorTest.cs" />
<Compile Include="$(MSBuildThisFileDirectory)CarouselAsync.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Bugzilla34561.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();
}
}
}