Как сделать жест длинным нажатием в формах Xamarin?

Не могли бы вы сообщить мне, как я могу распознать жест длинного нажатия в приложении Xamarin Forms?

За несколько дней до того, как я использовал TapGestureRecognizer

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

Но я не знаю, как сделать длинный жест прессы по этой теме с форума xamarin

Это должно выглядеть примерно так, но это не работает.

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
        }
 });

Как установить мой собственный метод обработчика?

8 ответов

Решение

В интернете я нашел решение. Есть несколько шагов, которые вы должны воспроизвести.

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

Это решение представляет собой гибрид ответов на форуме xamarin форм и презентации Touch and Gestures от Telerik.

Вы можете сделать это кросс-платформенным способом, прикрепив приведенное ниже поведение, пока оно 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);
            button.Pressed += Button_Pressed;
            button.Released += Button_Released;
        }

        protected override void OnDetachingFrom(Button button)
        {
            base.OnDetachingFrom(button);
            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>

Используйте пакет nuget XLabs.Forms, который делает длинное нажатие и другие жесты только в коде 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;
    }

Недавно я столкнулся с этой проблемой и нашел полезный пост на тему https://alexdunn.org/2017/12/27/xamarin-tip-xamarin-forms-long-press-effect/

Это использует RoutingEffect и проходит пример того, как создать реализацию для iOS и Android. Простота этого позволяет вам прикрепить его к любому представлению в вашем приложении без воссоздания кода.

У меня была аналогичная проблема в проекте .NET MAUI. В моем сценарии у меня есть кнопка, которую можно просто нажать для выполнения команды или долго нажимать для выполнения другой команды.

Я начал решать свою проблему с решения Зафара (которое мне показалось очень простым), но помимо проблемы BindingContext я нашел еще одну;

Проще говоря, когда я нажимаю кнопку, запускалась либо команда LongPress, либо простая команда щелчка, поэтому я внес некоторые изменения в решение Зафара для достижения своей цели, вот мой код:

      public class LongPressBehavior : Behavior<Button>
{
    private readonly object _syncObject = new object();
    private Timer _timer;
    private TimeSpan _pressDuration = new TimeSpan(0, 0, 0, 0, 500);

    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));

    public object CommandParameter
    {
        get => GetValue(CommandParameterProperty);
        set => SetValue(CommandParameterProperty, value);
    }

    public ICommand Command
    {
        get => (ICommand)GetValue(CommandProperty);
        set => SetValue(CommandProperty, value);
    }

    protected override void OnAttachedTo(Button button)
    {
        base.OnAttachedTo(button);
        BindingContext = button.BindingContext;
        button.Pressed += OnPressed;
        button.Released += OnReleased;
        button.BindingContextChanged += OnBindingContextChanged;
    }

    protected override void OnDetachingFrom(Button button)
    {
        base.OnDetachingFrom(button);
        BindingContext = null;
        button.Pressed -= OnPressed;
        button.Released -= OnReleased;
        button.BindingContextChanged -= OnBindingContextChanged;
    }

    private void OnBindingContextChanged(object sender, EventArgs e)
    {
        if (sender is Button button) BindingContext = button.BindingContext;
    }

    private void DeInitializeTimer()
    {
        lock (_syncObject)
        {
            if (_timer == null) return;                
            _timer.Change(Timeout.Infinite, Timeout.Infinite);
            _timer.Dispose();
            _timer = null;

            _pressDuration = new TimeSpan(0, 0, 0, 0, 500);
        }
    }

    private void InitializeTimer()
    {
        lock (_syncObject)
        {
            _timer = new Timer(Timer_Elapsed, null, 500, 500);
        }
    }

    private void OnPressed(object sender, EventArgs e)
    {
        InitializeTimer();

        var button = sender as Button;
        if (button.Command != null) button.Command.Execute(false);
    }

    private void OnReleased(object sender, EventArgs e)
    {
        var button = sender as Button;
        if (_pressDuration > TimeSpan.Zero && button.Command != null) button.Command.Execute(true);

        DeInitializeTimer();
    }

    public LongPressBehavior() { }

    private void Timer_Elapsed(object state)
    {
        _pressDuration = _pressDuration.Subtract(new TimeSpan(0, 0, 0, 0, 250));
        if (_pressDuration <= TimeSpan.Zero && Command != null && Command.CanExecute(CommandParameter))
        {
            Command.Execute(CommandParameter);
        }
    }
}

Моя идея основана на значении TimeSpan, которое уменьшается на желаемую продолжительность длительного нажатия. Когда я нажимаю кнопку, она запускает команду кнопки с ложным параметром, чтобы заблокировать ее выполнение. При отпускании кнопки, если TimeSpan больше нуля (поэтому длительное нажатие не завершено), кнопка запускает команду с истинным параметром. Когда TimeSpan достигает нулевого значения, он запускает команду LongPress.

Вот RelayCommand:

      [RelayCommand]
    public async void AddNewTimeStamp(bool? execute)
    {
        if (execute != true) return;

        // command logic and so on...
    }

Надеюсь, мое решение кому-то поможет!

//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);

Опубликованный код из @zafar работает, если вы регистрируете событие BindingContextChanged. (Мой пост - это всего лишь добавление к исходному посту от @zafar.)

Проблема была: при использовании CommandParameter="{Binding .}" результирующий параметр всегда был нулевым.

Вам необходимо зарегистрировать событие BindingContextChanged в функции OnAttachedTo.

        [...]
        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
        }
        [...]

Извините за ошибки, это мой первый пост (недостаточно репутации для комментирования).

Чтобы это работало на iOS должным образом, вам нужно использовать XLabs.Forms.XFormsAppiOS.Init(); в вашем файле AppDelegate.cs непосредственно перед LoadApplication(новое приложение ()); заявление.

Другие вопросы по тегам