web-dev-qa-db-ja.com

Xamarin.Forms-ボタンが押され、リリースされたイベント

ボタン押して離したのときにイベントをトリガーしたいのですが、Xamarin.FormsでClickイベントしか見つかりません。

この機能を使用するには、いくつかの回避策が必要だと思います。私の基本的なニーズは、ボタンが押されたときにプロセスを開始し、離されたときに停止することです。非常に基本的な機能のようですが、現在Xamarin.Formsにはありません。

ボタンでTapGestureRecognizerを試しましたが、ボタンでクリックイベントのみが発生しています。

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で機能している必要があることを覚えておいてください。

12
umair.ali

最後に、@ Jasonによって提案された解決策を得ました。さあ行こう...

  1. イベント処理機能を備えたPCLプロジェクトでXamarin.Forms.Buttonのサブクラスを作成します

    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);
        }
    }
    
  2. それぞれのプロジェクトでプラットフォーム固有のボタンレンダラーを作成する

    Andoridの場合

    [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();
                };
            }
        }
    }
    
  3. ページにカスタムボタンをインスタンス化する

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

これが誰かを助けることを願っています:)

22
umair.ali

これは、本格的なカスタムレンダラーの代わりにエフェクトを使用して行うこともできます。その方法の説明については、この投稿を参照してください。

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>
7
GBreen12

Xamarin.Forms 2.4.0以降、イベント Pressed および Released がそのまま提供されます( [ 〜#〜] pr [〜#〜] )。

注:トランシーバーエフェクトを実現するには、Device.BeginInvokeOnMainThread(またはPrismのIDeviceServiceを介して)結果のアクションを呼び出してReleasedイベントが呼び出されるようにします。そうしないと、UIスレッドがブロックされる可能性があります。
または、イベントハンドラーをasyncおよびawaitの呼び出しとして宣言して、UIスレッドが占有されないようにすることができます。

3
Shimmy
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();
        }
    };
2
Omid Farvid

XamarinでPressedおよびReleasedイベントをインターセプトするために、 この公式ガイド で説明されているように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アクションを区別するPressedReleasedExited...

N.B Effectsは、StackLayoutだけでなく、さまざまなコンポーネントで使用できます。

1
Marc_Alx