Xamarin.Forms - нажатие кнопки и выход

Я хочу, чтобы мое событие запускалось при нажатии и отпускании кнопки , но я могу найти Click событие в Xamarin.Forms.

Я считаю, что для обеспечения этой функциональности должна быть какая-то работа. Моя основная потребность - начать процесс при нажатии кнопки и остановке при отпускании. Кажется, это очень простая функция, но Xamarin.Forms не имеет ее прямо сейчас.

Я попробовал TapGestureRecognizer на кнопке, но кнопка запускает только событие click.

MyButton.Clicked += (sender, args) =>
{
  Log.V(TAG, "CLICKED");
};

var tapGestureRecognizer = new TapGestureRecognizer();
tapGestureRecognizer.Tapped += (s, e) => {
    Log.V(TAG, "TAPPED");
};
MyButton.GestureRecognizers.Add(tapGestureRecognizer);

Имейте в виду, что мне нужны эти события для работы в Android и iOS togather.

Ответ 1

Наконец, я получил решение, предложенное @Jason. Здесь мы идем...

  • Создайте подкласс класса Xamarin.Forms.Button в проекте PCL с событием возможности обработки

    public class CustomButton : Button
    {
        public event EventHandler Pressed;
        public event EventHandler Released;
    
        public virtual void OnPressed()
        {
          Pressed?.Invoke(this, EventArgs.Empty);
        }
    
        public virtual void OnReleased()
        {
          Released?.Invoke(this, EventArgs.Empty);
        }
    }
    
  • Создайте рендеринг кнопки конкретной платформы в соответствующем проекте

    Для Андорида

    [assembly: ExportRenderer(typeof(Button), typeof(CustomButtonRenderer))]
    namespace WalkieTalkie.Droid.Renderer
    {
        public class CustomButtonRenderer : ButtonRenderer
        {
            protected override void OnElementChanged(ElementChangedEventArgs<Xamarin.Forms.Button> e)
            {
                base.OnElementChanged(e);
    
                var customButton = e.NewElement as CustomButton;
    
                var thisButton = Control as Android.Widget.Button;
                thisButton.Touch += (object sender, TouchEventArgs args) =>
                {
                    if (args.Event.Action == MotionEventActions.Down)
                    {
                        customButton.OnPressed();
                    }
                    else if (args.Event.Action == MotionEventActions.Up)
                    {
                        customButton.OnReleased();
                    }
                };
            }
        }
    }
    

    Для IOS

    [assembly: ExportRenderer(typeof(CustomButton), typeof(CustomButtonRenderer))]
    namespace WalkieTalkie.iOS.Renderer
    {
        public class CustomButtonRenderer : ButtonRenderer
        {
            protected override void    OnElementChanged(ElementChangedEventArgs<Xamarin.Forms.Button> e)
            {
                base.OnElementChanged(e);
    
                var customButton = e.NewElement as CustomButton;
    
                var thisButton = Control as UIButton;
                thisButton.TouchDown += delegate
                {
                    customButton.OnPressed();
                };
                thisButton.TouchUpInside += delegate
                {
                    customButton.OnReleased();
                };
            }
        }
    }
    
  • Создайте свою пользовательскую кнопку на своей странице

    var myButton = new CustomButton
    {
        Text = "CustomButton",
        HorizontalOptions = LayoutOptions.FillAndExpand
    };
    myButton.Pressed += (sender, args) =>
    {
        System.Diagnostics.Debug.WriteLine("Pressed");
    };
    myButton.Released += (sender, args) =>
    {
         System.Diagnostics.Debug.WriteLine("Pressed");
    };
    

Надеюсь, что это поможет кому-то:)

Ответ 2

Это также можно сделать с эффектом, а не с полноценным визуализатором. См. Это сообщение для объяснения, как это сделать:

https://alexdunn.org/2017/12/27/xamarin-tip-xamarin-forms-long-press-effect/

В случае, если сообщение всегда уходит, вот код, который вы можете реализовать:

В совместном проекте:

/// <summary>
/// Long pressed effect. Used for invoking commands on long press detection cross platform
/// </summary>
public class LongPressedEffect : RoutingEffect
{
    public LongPressedEffect() : base("MyApp.LongPressedEffect")
    {
    }

    public static readonly BindableProperty CommandProperty = BindableProperty.CreateAttached("Command", typeof(ICommand), typeof(LongPressedEffect), (object)null);
    public static ICommand GetCommand(BindableObject view)
    {
        return (ICommand)view.GetValue(CommandProperty);
    }

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


    public static readonly BindableProperty CommandParameterProperty = BindableProperty.CreateAttached("CommandParameter", typeof(object), typeof(LongPressedEffect), (object)null);
    public static object GetCommandParameter(BindableObject view)
    {
        return view.GetValue(CommandParameterProperty);
    }

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

В Android:

[assembly: ResolutionGroupName("MyApp")]
[assembly: ExportEffect(typeof(AndroidLongPressedEffect), "LongPressedEffect")]
namespace AndroidAppNamespace.Effects
{
    /// <summary>
    /// Android long pressed effect.
    /// </summary>
    public class AndroidLongPressedEffect : PlatformEffect
    {
        private bool _attached;

        /// <summary>
        /// Initializer to avoid linking out
        /// </summary>
        public static void Initialize() { }

        /// <summary>
        /// Initializes a new instance of the
        /// <see cref="T:Yukon.Application.AndroidComponents.Effects.AndroidLongPressedEffect"/> class.
        /// Empty constructor required for the odd Xamarin.Forms reflection constructor search
        /// </summary>
        public AndroidLongPressedEffect()
        {
        }

        /// <summary>
        /// Apply the handler
        /// </summary>
        protected override void OnAttached()
        {
            //because an effect can be detached immediately after attached (happens in listview), only attach the handler one time.
            if (!_attached)
            {
                if (Control != null)
                {
                    Control.LongClickable = true;
                    Control.LongClick += Control_LongClick;
                }
                else
                {
                    Container.LongClickable = true;
                    Container.LongClick += Control_LongClick;
                }
                _attached = true;
            }
        }

        /// <summary>
        /// Invoke the command if there is one
        /// </summary>
        /// <param name="sender">Sender.</param>
        /// <param name="e">E.</param>
        private void Control_LongClick(object sender, Android.Views.View.LongClickEventArgs e)
        {
            Console.WriteLine("Invoking long click command");
            var command = LongPressedEffect.GetCommand(Element);
            command?.Execute(LongPressedEffect.GetCommandParameter(Element));
        }

        /// <summary>
        /// Clean the event handler on detach
        /// </summary>
        protected override void OnDetached()
        {
            if (_attached)
            {
                if (Control != null)
                {
                    Control.LongClickable = true;
                    Control.LongClick -= Control_LongClick;
                }
                else
                {
                    Container.LongClickable = true;
                    Container.LongClick -= Control_LongClick;
                }
                _attached = false;
            }
        }
    }
}

В iOS:

[assembly: ResolutionGroupName("MyApp")]
[assembly: ExportEffect(typeof(iOSLongPressedEffect), "LongPressedEffect")]
namespace iOSNamespace.Effects
{
    /// <summary>
    /// iOS long pressed effect
    /// </summary>
    public class iOSLongPressedEffect : PlatformEffect
    {
        private bool _attached;
        private readonly UILongPressGestureRecognizer _longPressRecognizer;
        /// <summary>
        /// Initializes a new instance of the
        /// <see cref="T:Yukon.Application.iOSComponents.Effects.iOSLongPressedEffect"/> class.
        /// </summary>
        public iOSLongPressedEffect()
        {
            _longPressRecognizer = new UILongPressGestureRecognizer(HandleLongClick);
        }

        /// <summary>
        /// Apply the handler
        /// </summary>
        protected override void OnAttached()
        {
            //because an effect can be detached immediately after attached (happens in listview), only attach the handler one time
            if (!_attached)
            {
                Container.AddGestureRecognizer(_longPressRecognizer);
                _attached = true;
            }
        }

        /// <summary>
        /// Invoke the command if there is one
        /// </summary>
        private void HandleLongClick()
        {
            var command = LongPressedEffect.GetCommand(Element);
            command?.Execute(LongPressedEffect.GetCommandParameter(Element));
        }

        /// <summary>
        /// Clean the event handler on detach
        /// </summary>
        protected override void OnDetached()
        {
            if (_attached)
            {
                Container.RemoveGestureRecognizer(_longPressRecognizer);
                _attached = false;
            }
        }

    }
}

В XAML

<Label Text="Long Press Me!" effects:LongPressedEffect.Command="{Binding ShowAlertCommand}" effects:LongPressedEffect.CommandParameter="{Binding .}">
    <Label.Effects>
        <effects:LongPressedEffect />
    </Label.Effects>
</Label>

Ответ 3

Button button = FindViewById (Resource.Id.myButton);
button.Touch += (object sender, View.TouchEventArgs e) =>
{
if (e.Event.Action == MotionEventActions.Up)
{
Toast.MakeText(this, "Key Up", ToastLength.Short).Show();
}
        if(e.Event.Action == MotionEventActions.Down)
        {
            Toast.MakeText(this, "Key Down", ToastLength.Short).Show();
        }
    };

Ответ 4

Начиная с Xamarin.Forms 2.4.0, события Pressed и Released предлагаются "из коробки" (см. PR).

Примечание: для достижения эффекта Walkie Talkie вы можете использовать Device.BeginInvokeOnMainThread (или через Prism IDeviceService) для вызова последующих действий, чтобы IDeviceService событие Released, в противном случае поток пользовательского интерфейса может быть заблокирован.
Кроме того, вы можете объявить обработчики событий как async и await ваших вызовов, чтобы сохранить поток пользовательского интерфейса незанятым.

Ответ 5

Чтобы перехватить событие Pressed и Released на Xamarin, я использовал свойство Effects как описано в этом официальном руководстве.

Это еще проще, используя TouchTracking.Forms.

Сначала добавьте библиотеку в ваш проект Forms (не требуется в проекте для конкретной платформы).

Затем используйте его в Xaml Asis

<StackLayout>
    <StackLayout.Effects>
        <tt:TouchEffect TouchAction="Handle_TouchAction" />
    </StackLayout.Effects>
    <Label Text="Sample"/>
</StackLayout>

С tt ссылаясь на:

xmlns:tt="clr-namespace:TouchTracking.Forms;assembly=TouchTracking.Forms"

Наконец, вы работаете в коде:

void Handle_TouchAction(object sender, TouchTracking.TouchActionEventArgs args)
{
    ;
}

Handle_TouchAction будет вызываться каждый раз, когда происходит действие касания, используйте args.Type чтобы различить действие Pressed, Released, Exited...

NB Effects доступен для различных компонентов, а не только для StackLayout.