【问题标题】:How to make long press gesture in Xamarin Forms?如何在 Xamarin Forms 中进行长按手势?
【发布时间】:2017-09-20 00:55:34
【问题描述】:

您能否告诉我如何识别 Xamarin Forms 应用程序中的长按手势?

前几天我用TapGestureRecognizer

TapGestureRecognizer imageTap = new TapGestureRecognizer();
imageTap.Tapped += (sender, args) => this.OnClickImage;
image.GestureRecognizers.Add(imageTap);

但我不知道如何根据这个thread from xamarin forum做出长按手势

它应该看起来像这样,但它不起作用。

var dumpParam = new RelayGesture((g, x) => DisplayAlert("Title", "Hello message", "Cancel"));

book.Cover.SetValue(Gestures.InterestsProperty, new GestureCollection() {
    new GestureInterest
        {
            GestureType = GestureType.LongPress
            GestureCommand = // what should I set?
            GestureParameter = dumpParam
        }
 });

如何设置我的自定义处理程序方法?

【问题讨论】:

  • @NoorAshuvo 是的。但我不知道在我的情况下实施它很热。我不使用XAML。几天前,我将TapGestureRecognizerTapped 事件与Xamarin.Froms.Image 一起使用,我将其添加到图像的GestureRecognizers,但我不知道如何将我的句柄方法设置为GestureInterest,这在上面的链接中进行了描述。你能举个例子吗?
  • 此请求正在此打开的Enhancement LongPressGestureRecognizer 中进行跟踪。
  • ... 在实现之前,AFAIK 最简单的解决方案是 Alex Dunn 的 RoutingEffect 长按方法,如 stackoverflow.com/a/53752460/199364 中所述
  • 您也可以考虑使用效果。 Alex Dunn 写了一篇很好的文章:c-sharpcorner.com/article/…

标签: xamarin xamarin.forms gesture


【解决方案1】:

上网我找到了解决方案。您应该重现几个步骤。

1) 继承你需要手势的控件(即如果你想给Xamarin.Forms.Image添加手势,创建你自己的ImageWithLongPressGesture类)。

public class ImageWithLongPressGesture : Xamarin.Forms.Image
{
    public EventHandler LongPressActivated;

    public void HandleLongPress(object sender, EventArgs e)
    {
        //Handle LongPressActivated Event
    }
}

2) 公开所需手势的公共事件。

3) 为每个平台创建一个渲染器。

4) 在渲染器中,处理手势并将它们冒泡到您的控件中。

[assembly: ExportRenderer(typeof(ImageWithLongPressGesture), typeof(LongPressGestureRecognizerImageRenderer))]
namespace App1.Droid.DroidRenderers
{
    public class LongPressGestureRecognizerImageRenderer : ImageRenderer
    {
        ImageWithLongPressGesture view;

        public LongPressGestureRecognizerImageRenderer()
        {
            this.LongClick += (sender, args) => {
                Toast.MakeText(this.Context, "Long press is activated.", ToastLength.Short).Show();
            };
        }

        protected override void OnElementChanged(ElementChangedEventArgs<Image> e)
        {
            base.OnElementChanged(e);

            if(e.NewElement != null)
            {
                view = e.NewElement as ImageWithLongPressGesture;
            }
        }
    }
}

此解决方案是 Telerik 的 answer on xamarin forms forumTouch and Gestures presentation 的混合体。

【讨论】:

  • 此解决方案不适用于 android。它适用于 iOS 完全正常,我在我的项目中使用它但是对于 android 什么都没有发生,手势永远不会被处理。渲染器是 100% 连接的,因为我已经能够让替代实现工作(但是它们不允许我将回调发送到框架,所以基本上没用)。关于如何让它发挥作用的任何想法?我使用的是框架而不是图像,但我确信这没关系。只是要清楚this.LongClick 被添加到每一帧中,只是从未触发过......
【解决方案2】:

利用 XLabs.Forms nuget 包,它只在 PCL 代码中进行长按和其他手势。 使用 XLabs.Forms 包将减少在各个平台中自定义渲染的需求... 在 .xaml 文件中添加 XAML 代码,在 .xaml.cs 文件中添加事件处理程序。 它在 Android 中运行良好..

<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
         xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
         x:Class="MultiImage.Page1"             
         xmlns:lc="clr-namespace:XLabs.Forms.Controls;assembly=XLabs.Forms"
         xmlns:lb="clr-namespace:XLabs.Forms.Behaviors;assembly=XLabs.Forms">
<ContentPage.Content>
    <lc:GesturesContentView ExcludeChildren="False" GestureRecognized="GesturesContentView_GestureRecognized">
         <lb:Gestures.Interests>
                <lb:GestureCollection>
                    <lb:GestureInterest GestureType="SingleTap"/>
                    <lb:GestureInterest GestureType="LongPress"/>
                    <lb:GestureInterest GestureType="DoubleTap"/>
                </lb:GestureCollection>
          </lb:Gestures.Interests>
          <Image Source="Myimage.png" Aspect="AspectFit" HeightRequest="100"/>
        </lc:GesturesContentView>
</ContentPage.Content>

C# 后端代码:

private void GesturesContentView_GestureRecognized(object sender, GestureResult e)
{           
    switch (e.GestureType)
    {
        case GestureType.LongPress:
            //Add code here
            break;
        case GestureType.SingleTap:
            // Add code here                    
            break;
        case GestureType.DoubleTap:
            // Add code here
            break;
        default:
            break;
    }

【讨论】:

  • 当我尝试使用此示例时, 标记带有红色下划线,并且我收到错误“无效类型:预期类型是 'BindableProperty',实际类型是 'GestureCollection”。知道问题可能是什么吗?
  • 确保您拥有最新版本的 Xlabs.forms,并且您已经定义了 Xlabs.forms.controls 和 Xlabs.forms.behaviour 的命名空间
【解决方案3】:
//To Add Programatically:

StackLayout _Containter = new StackLayout();
StackLayout _StackLayout = new StackLayout();
 _StackLayout.Children.Add(new Label(){Text="Execute Me"});

GesturesContentView Gv = new GesturesContentView();
_StackLayout.SetValue(XLabs.Forms.Behaviors.Gestures.InterestsProperty, new GestureCollection() {
                      new GestureInterest() { GestureType = GestureType.SingleTap },
                      new GestureInterest() { GestureType = GestureType.LongPress },
                      new GestureInterest() { GestureType = GestureType.DoubleTap }
            });
Gv.GestureRecognized += Gv_GestureRecognized;
Gv.ExcludeChildren = false;
    Gv.Content = _StackLayout;
_Containter.Children.Add(Gv);

【讨论】:

  • 注意:这使用了不再积极维护的 Xamarin-Forms-Labs。不幸的是,似乎还没有任何受支持的替代品。
  • 此请求正在此打开的Enhancement LongPressGestureRecognizer 中进行跟踪。
【解决方案4】:

为了让它在 iOS 上正常工作,您需要使用 XLabs.Forms.XFormsAppiOS.Init();在 LoadApplication(new App()); 之前的 AppDelegate.cs 文件中;声明。

【讨论】:

    【解决方案5】:

    只要是Xamarin.Forms.Button或它的子类型,你就可以通过附加以下行为来跨平台。

    using System;
    using System.Threading;
    using System.Windows.Input;
    using Xamarin.Forms;
    
    namespace App.Controls.Behaviors
    {
        public class LongPressBehavior : Behavior<Button>
        {
            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(Button button)
            {
                base.OnAttachedTo(button);
                this.BindingContext = button.BindingContext;
                button.Pressed += Button_Pressed;
                button.Released += Button_Released;
            }
    
            protected override void OnDetachingFrom(Button button)
            {
                base.OnDetachingFrom(button);
                this.BindingContext = null;
                button.Pressed -= 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)
            {
                _isReleased = false;
                InitializeTimer();
            }
    
            private void Button_Released(object sender, EventArgs e)
            {
                _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);
            }
        }
    }
    

    在 XAML 用户界面中:

     <Button x:Name="MyButton" Text="Long Press Me!">
       <Button.Behaviors>
         <behaviors:LongPressBehavior LongPressed="MyButton_LongPressed"/>
       </Button.Behaviors>
     </Button>
    

    带有命令绑定的 XAML UI:

     <Button x:Name="MyButton" Text="Long Press Me!">
       <Button.Behaviors>
         <behaviors:LongPressBehavior Command="{Binding CommandInViewModel}"/>
       </Button.Behaviors>
     </Button>
    

    【讨论】:

    • 这不仅解决了我的问题,还教会了我如何在 Xamarin 中做一些我什至不知道可能的事情。这比我回答的问题要多。
    • 能否请您扩展 XAML UI 中的示例,因为我想通过 'Command={Binding Command_in_VM}' 使用命令,但它没有在按钮中设置任何命令,(即命令 == null) 并且没有执行发生。虽然相同的代码适用于常规 Button..
    • @xy 命令绑定不起作用的原因是 LongPressCommandBehavior 上没有绑定上下文。您必须在 OnAttachedTo 方法中将 LongPressCommandBehavior 上的 BindingContext 属性设置为按钮的 BindingContext。我已经更新了源代码并扩展了 XAML UI 示例来解决这个问题。
    • 当我尝试使用它时,它在 ios 上崩溃 - 这仍然是实现它的好方法吗?我看到人们也尝试通过效果而不是行为来做到这一点 - 这是更好的选择吗?
    • 我建议在这里做一个小修改。您应该在OnAttached() 中分配一个OnBindingContextChanged 事件处理程序来处理绑定上下文的问题。还记得在OnDetached() 中删除它以进行清理。我将尝试编辑答案以反映这一点。但是像这样:`private void OnBindingContextChanged(object sender, EventArgs e) { var button = sender as Button; BindingContext = 按钮?.BindingContext; }` 并在 OnAttached() 中添加 button.BindingContextChanged += OnBindingContextChanged; 并在 OnDetached 中使用 -= 删除
    【解决方案6】:

    我最近遇到了这个问题,发现了一篇关于https://alexdunn.org/2017/12/27/xamarin-tip-xamarin-forms-long-press-effect/主题的有用帖子

    这使用了RoutingEffect,并通过一个示例来说明如何创建 iOS 和 Android 实现。这样做的简单性允许您将其附加到应用程序中的任何视图,而无需重新创建代码。

    【讨论】:

    • 我认为这是最好的答案。
    • 我用过,但是绑定命令时出现空引用异常。有没有办法我可以使用事件来代替?
    【解决方案7】:

    如果您注册 BindingContextChanged 事件,则来自@zafar 的发布代码有效。 (我的帖子只是对@zafar 原始帖子的补充。)

    问题是: 如果使用CommandParameter="{Binding .}",则结果参数始终为空。

    您需要在 OnAttachedTo 函数中注册 BindingContextChanged 事件。

            [...]
            protected override void OnAttachedTo(Button button)
            {
                base.OnAttachedTo(button);
                this.BindingContext = button.BindingContext;
                button.BindingContextChanged += handleBindingContextChanged; //this was missing
                button.Pressed += Button_Pressed;
                button.Released += Button_Released;
            }
    
            private void handleBindingContextChanged(object sender, EventArgs e)
            {
                this.BindingContext = ((Button)sender).BindingContext;
            }
    
            protected override void OnDetachingFrom(Button button)
            {
                base.OnDetachingFrom(button);
                this.BindingContext = null;
                button.Pressed -= Button_Pressed;
                button.Released -= Button_Released;
                button.BindingContextChanged -= handleBindingContextChanged; //also don't forget this
            }
            [...]
    

    抱歉,这是我的第一篇文章(没有足够的评论声誉)。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2019-05-13
      • 2019-08-26
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2018-01-21
      • 2015-07-10
      相关资源
      最近更新 更多