简体   繁体   中英

Xamarin.Android Tap Gesture and Long Press Gesture not working together

I have created a custom effect by subclassing RoutingEffect in order to allow LongPressGesture for both iOS and Android in my Xamarin project.

I am using this effect on an Image in in the XAML of my shared project, and this same Image is also using a TapGesture , see code below:

                <Image x:Name="TapRight" Grid.Row="4" Grid.Column="2"  Source="right64" 
                   VerticalOptions="FillAndExpand" HorizontalOptions="FillAndExpand"
                   IsEnabled="{Binding RightEnabled}"
                   Opacity="{Binding RightEnabled, Converter={StaticResource OpacityConverter}}"
                   effects:LongPressEffect.Command="{Binding LongPressGestureCommand}"
                   effects:LongPressEffect.CommandParameter="{x:Static common:NavType.Right}">
                <Image.GestureRecognizers>
                    <TapGestureRecognizer 
                        Command="{Binding TapGestureNavCommand}" 
                        NumberOfTapsRequired="1"
                        CommandParameter="{x:Static common:NavType.Right}"/>
                </Image.GestureRecognizers>
                <Image.Effects>
                        <effects:LongPressEffect></effects:LongPressEffect>
                </Image.Effects>
            </Image>

This works fine for iOS (I get separate functionality when I tap vs when I long press the image), however for Android , it only allows me to do Long Press, and does not execute the command for the TapGesture , any ideas on how to fix this?

NOTE: If I use a Button instead of an Image it works fine. However, I would really like to use an Image .

I have added more code below for reference:

Code for the effect in shared project:

using System.Windows.Input;
using Xamarin.Forms;


namespace MyApp.Effects
{

    public class LongPressEffect : RoutingEffect
    {
        public LongPressEffect() : base("Xamarin.LongPressEffect")
        {

        }

        public static readonly BindableProperty CommandProperty =
         BindableProperty.CreateAttached("Command",
             typeof(ICommand),
             typeof(LongPressEffect),
             (object)null,
             propertyChanged: OnCommandChanged);

        public static ICommand GetCommand(BindableObject view)
        {
            return (ICommand)view.GetValue(CommandProperty);
        }

        public static void SetCommand(BindableObject view, ICommand value)
        {
            view.SetValue(CommandProperty, value);
        }

        static void OnCommandChanged(BindableObject bindable, object oldValue, object newValue)
        {
            var view = bindable as View;
            if (view == null)
            {
                return;
            }
            ICommand command = (ICommand)newValue;
            if (command != null)
            {
                view.SetValue(CommandProperty, command);
            }

        }

        public static readonly BindableProperty CommandParameterProperty =
            BindableProperty.CreateAttached("CommandParameter",
                typeof(object),
                typeof(LongPressEffect),
                (object)null,
                propertyChanged: OnCommandParameterChanged);

        public static object GetCommandParameter(BindableObject view)
        {
            return (object)view.GetValue(CommandParameterProperty);
        }

        public static void SetCommandParameter(BindableObject view, object value)
        {
            view.SetValue(CommandParameterProperty, value);
        }

        static void OnCommandParameterChanged(BindableObject bindable, object oldValue, object newValue)
        {
            var view = bindable as View;
            if (view == null)
            {
                return;
            }
            object commandParameter = (object)newValue;
            if (commandParameter != null)
            {
                view.SetValue(CommandParameterProperty, commandParameter);
            }
        }
    }
}

Code for effect in iOS:

using System;
using System.ComponentModel;
using MyApp.Effects;
using UIKit;
using Xamarin.Forms;
using Xamarin.Forms.Platform.iOS;

[assembly: ResolutionGroupName("Xamarin")]
[assembly:ExportEffect (typeof(MyApp.iOS.Effects.LongPressEffect), "LongPressEffect")]
namespace MyApp.iOS.Effects
{
    public class LongPressEffect : PlatformEffect
    {
        private readonly UILongPressGestureRecognizer _longPressGestureRecognizer;
        private bool _attached;
        public LongPressEffect()
        {
            _longPressGestureRecognizer = new UILongPressGestureRecognizer(HandleLongClick);
            _attached = false;

        }

        protected override void OnAttached()
        {
            if (!_attached)
            {
                Container.AddGestureRecognizer(_longPressGestureRecognizer);
                _attached = true;
            }
        }

        private void HandleLongClick()
        {
            if (_longPressGestureRecognizer.State == UIGestureRecognizerState.Ended)
                // Only execute when the press is ended.
            {
                var command = MyApp.Effects.LongPressEffect.GetCommand(Element);
                command?.Execute(MyApp.Effects.LongPressEffect.GetCommandParameter(Element));
            }
        }

        protected override void OnDetached()
        {
            if (_attached)
            {
                Container.RemoveGestureRecognizer(_longPressGestureRecognizer);
                _attached = false;
            }
        }

        protected override void OnElementPropertyChanged(PropertyChangedEventArgs args)
        {
            base.OnElementPropertyChanged(args);
        }
    }
}

Code for effect in Android:

using System;
using System.ComponentModel;
using Xamarin.Forms;
using Xamarin.Forms.Platform.Android;

[assembly: ResolutionGroupName("Xamarin")]
[assembly: ExportEffect(typeof(MyApp.Droid.Effects.LongPressEffect), "LongPressEffect")]
namespace MyApp.Droid.Effects
{
    public class LongPressEffect: PlatformEffect
    {
        private bool _attached;
        public static void Initialize() { }
        public LongPressEffect()
        {
            _attached = false;

        }

        protected override void OnAttached()
        {
            Console.WriteLine("Invoking long click command...");
            //throw new NotImplementedException();
            if (!_attached) {
                if (Control != null)
                {
                    Control.LongClickable = true;
                    Control.LongClick += HandleLongClick;
                }
                _attached = true;
            }
        }

        private void HandleLongClick(object sender, Android.Views.View.LongClickEventArgs e) {
            Console.WriteLine("Invoking long click command...");
            var command = MyApp.Effects.LongPressEffect.GetCommand(Element);
            command?.Execute(MyApp.Effects.LongPressEffect.GetCommandParameter(Element));

        }

        protected override void OnDetached()
        {
            //throw new NotImplementedException();
            if (_attached) {
                if (Control != null) {
                    Control.LongClickable = true;
                    Control.LongClick -= HandleLongClick;
                }
                _attached = false;
            }
        }

        protected override void OnElementPropertyChanged(PropertyChangedEventArgs args)
        {
            base.OnElementPropertyChanged(args);
        }
    }
}

This is a bug in Xamarin, more details can be found here

As a workaround I have used an ImageButton or Android and Image for IOS and made the visibility platform dependent. my XAML now looks like this:

<ImageButton x:Name="Tap" Grid.Row="4" Grid.Column="2" Source="right64" 
       IsEnabled="{Binding RightEnabled}"
       Opacity="{Binding RightEnabled, Converter={StaticResource OpacityConverter}}"     
       effects:LongPressEffect.Command="{Binding LongPressGestureCommand}"
       effects:LongPressEffect.CommandParameter="{x:Static common:NavType.Right}"
       Command="{Binding TapGestureNavCommand}"
       CommandParameter="{x:Static common:NavType.Right}">
       <ImageButton.IsVisible>
            <OnPlatform x:TypeArguments="x:Boolean"
                        iOS="False"
                        Android="True"/>
       </ImageButton.IsVisible>
       <ImageButton.Effects>
           <effects:LongPressEffect></effects:LongPressEffect>
       </ImageButton.Effects>
</ImageButton>

<!--Due to different behaviour on platform(s) different views were needed for different platforms.-->

<Image x:Name="TapIOS" Grid.Row="4" Grid.Column="2"  Source="right64" 
        IsEnabled="{Binding RightEnabled}"
        Opacity="{Binding RightEnabled, Converter={StaticResource OpacityConverter}}"    
        effects:LongPressEffect.Command="{Binding LongPressGestureCommand}"
        effects:LongPressEffect.CommandParameter="{x:Static common:NavType.Right}">
    <Image.IsVisible>
        <OnPlatform x:TypeArguments="x:Boolean"
                    iOS="True"
                    Android="False"/>
    </Image.IsVisible>
    <Image.GestureRecognizers>
         <TapGestureRecognizer
             NumberOfTapsRequired="1"
             Command="{Binding TapGestureNavCommand}"
             CommandParameter="{x:Static common:NavType.Right}"/>
    </Image.GestureRecognizers>
    <Image.Effects>
          <effects:LongPressEffect></effects:LongPressEffect>
    </Image.Effects>
</Image>

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM