Categories
Xamarin

Tap, Tap, Tap, Tap, Bang!

There are limitations to the write-once run everywhere approach of Xamarin Forms. One of these is the subtle difference in Button behaviour across platforms. One of our testers pointed out that you could rapidly tap a Button on Android and the handler would be called multiple times. Since the Tap was initiating a page navigation this would lead to multiple copies of the page being added to the back-stack.

The workaround necessary is simply to add a timeout to ensure that the Tap event is not handled more frequently than a specified timeout. We chose 500ms but your use-case may vary. I created a renderer derived from the built-in ButtonRender and searched a way to intercept tap events. It turns out that a lot of the methods which can be overridden are never called but I found a solution through the excitingly named OnFilterTouchEventForSecurity. The resulting renderer code is included in the following Gist and can be added to your Android project to enforce this behaviour.

using Android.Views;
[assembly: Xamarin.Forms.ExportRenderer(typeof(Xamarin.Forms.Button), typeof(InTheHand.Forms.Platform.Android.SingleTapButtonRenderer))]
namespace InTheHand.Forms.Platform.Android
{
public sealed class SingleTapButtonRenderer : Xamarin.Forms.Platform.Android.ButtonRenderer
{
bool justClicked = false;
public override bool OnFilterTouchEventForSecurity(MotionEvent e)
{
if(e.Action == MotionEventActions.Up)
{
if(justClicked)
{
return false;
}
else
{
justClicked = true;
global::System.Threading.Tasks.Task.Run(async () =>
{
// reset after a timeout
await global::System.Threading.Tasks.Task.Delay(500);
justClicked = false;
});
}
return true;
}
return base.OnFilterTouchEventForSecurity(e);
}
}
}

By Peter Freeman Foot

Microsoft Windows Development MVP