首页 > 解决方案 > Xamarin 中是否有针对标签上的 GestureRecognizer 的新闻和发布活动?

问题描述

我正在尝试检测 Xamarin 中的新闻和发布事件,当用户点击应用程序中的标签时,以实现长按手势。

300

我已经从另一个 StackOverflow 问题创建了一个 LongPressBehavior 类,但标签没有附加到它们的按下和释放事件。

我可以使用按钮,但我仍然需要具有 URL 链接的格式化文本 (FormattedString),用户可以单击这些链接导航到网站。

C#

public class LongPressBehavior : BehaviorBase<ExtendedLabel>
    {
        private readonly object _syncObject = new object();
        private const int Duration = 1000;

        //timer to track long press
        private Timer _timer;
        //the timeout value for long press
        private readonly int _duration;
        //whether the button was released after press
        private volatile bool _isReleased;

        /// <summary>
        /// Occurs when the associated button is long pressed.
        /// </summary>
        public event EventHandler LongPressed;

        public static readonly BindableProperty CommandProperty = BindableProperty.Create(nameof(Command),
            typeof(ICommand), typeof(LongPressBehavior), default(ICommand));

        public static readonly BindableProperty CommandParameterProperty =
            BindableProperty.Create(nameof(CommandParameter), typeof(object), typeof(LongPressBehavior));

        /// <summary>
        /// Gets or sets the command parameter.
        /// </summary>
        public object CommandParameter
        {
            get => GetValue(CommandParameterProperty);
            set => SetValue(CommandParameterProperty, value);
        }

        /// <summary>
        /// Gets or sets the command.
        /// </summary>
        public ICommand Command
        {
            get => (ICommand)GetValue(CommandProperty);
            set => SetValue(CommandProperty, value);
        }

        protected override void OnAttachedTo(ExtendedLabel button)
        {
            base.OnAttachedTo(button);
            this.BindingContext = button.BindingContext;
            button.Clicked += Button_Pressed;
            //button.Released += Button_Released;
        }

        protected override void OnDetachingFrom(ExtendedLabel button)
        {
            base.OnDetachingFrom(button);
            this.BindingContext = null;
            button.Clicked -= Button_Pressed;
            //button.Released -= Button_Released;
        }

        /// <summary>
        /// DeInitializes and disposes the timer.
        /// </summary>
        private void DeInitializeTimer()
        {
            lock (_syncObject)
            {
                if (_timer == null)
                {
                    return;
                }
                _timer.Change(Timeout.Infinite, Timeout.Infinite);
                _timer.Dispose();
                _timer = null;
                Debug.WriteLine("Timer disposed...");
            }
        }

        /// <summary>
        /// Initializes the timer.
        /// </summary>
        private void InitializeTimer()
        {
            lock (_syncObject)
            {
                _timer = new Timer(Timer_Elapsed, null, _duration, Timeout.Infinite);
            }
        }

        private void Button_Pressed(object sender, EventArgs e)
        {
            Debug.WriteLine("Pressed");
            _isReleased = false;
            InitializeTimer();
        }

        private void Button_Released(object sender, EventArgs e)
        {
            Debug.WriteLine("Released");
            _isReleased = true;
            DeInitializeTimer();
        }

        protected virtual void OnLongPressed()
        {
            var handler = LongPressed;
            handler?.Invoke(this, EventArgs.Empty);
            if (Command != null && Command.CanExecute(CommandParameter))
            {
                Command.Execute(CommandParameter);
            }
        }

        public LongPressBehavior()
        {
            _isReleased = true;
            _duration = Duration;
        }

        public LongPressBehavior(int duration) : this()
        {
            _duration = duration;
        }

        private void Timer_Elapsed(object state)
        {
            DeInitializeTimer();

            if (_isReleased)
            {
                return;
            }
            Device.BeginInvokeOnMainThread(OnLongPressed);
        }
    }
public class ExtendedLabel : Label
    {
        private event EventHandler Pressed;
        private event EventHandler Released;

        public string Name
        {
            get; set;
        }

        public void DoClick()
        {
            Pressed.Invoke(this, null);
        }

        public event EventHandler Clicked
        {
            add
            {
                lock (this)
                {
                    Pressed += value;

                    TapGestureRecognizer g = new TapGestureRecognizer();

                    g.Tapped += (s, e) => Pressed?.Invoke(s, e);

                    GestureRecognizers.Add(g);
                }
            }
            remove
            {
                lock (this)
                {
                    Pressed -= value;

                    GestureRecognizers.Clear();
                }
            }
        }

        public event EventHandler UnClicked
        {
            add
            {
                lock (this)
                {
                    Released += value;

                    TapGestureRecognizer g = new TapGestureRecognizer();

                    g.Tapped += (s, e) => Released?.Invoke(s, e);

                    GestureRecognizers.Add(g);
                }
            }
            remove
            {
                lock (this)
                {
                    Released -= value;

                    GestureRecognizers.Clear();
                }
            }
        }
    }

XAML

<controls:ExtendedLabel HorizontalOptions="Fill" HorizontalTextAlignment="End"  TextColor="White" FormattedText="{Binding OutFormattedBody}">
                   <controls:ExtendedLabel.Behaviors>
                       <behaviors:LongPressBehavior LongPressed="OnClick"/>
                   </controls:ExtendedLabel.Behaviors>
                </controls:ExtendedLabel>

我希望能够在标签上点击并按住大约一秒钟以触发 LongPress 事件,但仍然能够单击可能位于 FormattedString 中的链接。

标签: c#androidiosxamlxamarin

解决方案


您可以尝试在每个平台上实现它的效果。首先,在 Forms 项目中定义效果类:

public class PressedEffect : RoutingEffect
{
    public PressedEffect() : base("MyApp.PressedEffect")
    {
    }

    public static readonly BindableProperty LongPressedCommandProperty = BindableProperty.CreateAttached("LongPressedCommand", typeof(ICommand), typeof(PressedEffect), (object)null);
    public static ICommand GetLongPressedCommand(BindableObject view)
    {
        return (ICommand)view.GetValue(LongPressedCommandProperty);
    }

    public static void SetLongPressedCommand(BindableObject view, ICommand value)
    {
        view.SetValue(LongPressedCommandProperty, value);
    }


    public static readonly BindableProperty LongParameterProperty = BindableProperty.CreateAttached("LongParameter", typeof(object), typeof(PressedEffect), (object)null);
    public static object GetLongParameter(BindableObject view)
    {
        return view.GetValue(LongParameterProperty);
    }

    public static void SetLongParameter(BindableObject view, object value)
    {
        view.SetValue(LongParameterProperty, value);
    }

    public static readonly BindableProperty LongRelesedCommandProperty = BindableProperty.CreateAttached("LongRelesedCommand", typeof(ICommand), typeof(PressedEffect), (object)null);
    public static ICommand GetLongRelesedCommand(BindableObject view)
    {
        return (ICommand)view.GetValue(LongRelesedCommandProperty);
    }

    public static void SetLongRelesedCommand(BindableObject view, ICommand value)
    {
        view.SetValue(LongRelesedCommandProperty, value);
    }
}

安卓实现

[assembly: ResolutionGroupName("MyApp")]
[assembly: ExportEffect(typeof(AndroidPressedEffect), "PressedEffect")]
namespace LongPressedDemo.Droid
{
    public class AndroidPressedEffect : PlatformEffect
    {
        private bool _attached;
        public AndroidPressedEffect()
        {
        }

        protected override void OnAttached()
        {
            if (!_attached)
            {
                if (Control != null)
                {
                    Control.LongClickable = true;
                    Control.Touch += Control_Touch;
                }
                else
                {
                    Container.LongClickable = true;
                    Container.Touch += Control_Touch;
                }
                _attached = true;
            }
        }

        private void Control_Touch(object sender, Android.Views.View.TouchEventArgs e)
        {
            if (e.Event.Action == MotionEventActions.Down)
            {
                var command = PressedEffect.GetLongPressedCommand(Element);
                command?.Execute(PressedEffect.GetLongParameter(Element));
            }
            else if (e.Event.Action == MotionEventActions.Up)
            {
                var command = PressedEffect.GetLongRelesedCommand(Element);
                command?.Execute(PressedEffect.GetLongParameter(Element));
            }
        }


        protected override void OnDetached()
        {
            if (_attached)
            {
                if (Control != null)
                {
                    Control.LongClickable = true;
                    Control.Touch -= Control_Touch;
                }
                else
                {
                    Container.LongClickable = true;
                    Container.Touch -= Control_Touch;
                }
                _attached = false;
            }
        }
    }
}

iOS 实现

[assembly: ResolutionGroupName("MyApp")]
[assembly: ExportEffect(typeof(iOSPressedEffect), "PressedEffect")]
namespace LongPressedDemo.iOS
{
    public class iOSPressedEffect : PlatformEffect
    {
        private bool _attached;
        private readonly UILongPressGestureRecognizer _longPressRecognizer;
        public iOSPressedEffect()
        {
            _longPressRecognizer = new UILongPressGestureRecognizer(HandleLongClick);
        }

        protected override void OnAttached()
        {
            if (!_attached)
            {
                if (Control != null)
                {
                    Control.AddGestureRecognizer(_longPressRecognizer);
                    Control.UserInteractionEnabled = true;
                }
                else
                {
                    Container.AddGestureRecognizer(_longPressRecognizer);
                }               
                _attached = true;
            }
        }

        private void HandleLongClick(UILongPressGestureRecognizer recognizer)
        {
            if (recognizer.State == UIGestureRecognizerState.Began)
            {
                var command = PressedEffect.GetLongPressedCommand(Element);
                command?.Execute(PressedEffect.GetLongParameter(Element));
            }
            else if (recognizer.State == UIGestureRecognizerState.Ended)
            {
                var command = PressedEffect.GetLongRelesedCommand(Element);
                command?.Execute(PressedEffect.GetLongParameter(Element));
            }
        }

        protected override void OnDetached()
        {
            if (_attached)
            {
                if (Control != null)
                {
                    Control.RemoveGestureRecognizer(_longPressRecognizer);
                }
                else
                {
                    Container.RemoveGestureRecognizer(_longPressRecognizer);
                }
                _attached = false;
            }
        }
    }
}

最后,像这样消耗这种效果:

<Label Text="Welcome to Xamarin.Forms!" 
    local:PressedEffect.LongPressedCommand="{Binding PressedCommand}" 
    local:PressedEffect.LongRelesedCommand="{Binding ReleasedCommand}">
    <Label.Effects>
        <local:PressedEffect/>
    </Label.Effects>
</Label>

推荐阅读