Xamarin.Forms - 按钮按下和释放事件
Xamarin.Forms - Button Pressed & Released Event
我希望我的事件在按钮 按下和释放 时触发,但我只能在 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 中一起工作。
终于得到了@Jason建议的解决方案。我们开始...
在 PCL 项目中创建 Xamarin.Forms.Button 的子 class,带有事件
处理能力
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");
};
希望这对某人有所帮助:)
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();
}
};
这也可以通过效果而不是完整的自定义渲染器来完成。请参阅此 post 以了解如何操作:
https://alexdunn.org/2017/12/27/xamarin-tip-xamarin-forms-long-press-effect/
以防 post 消失,这里是您可以实现的代码:
In shared project:
/// <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);
}
}
In 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;
}
}
}
}
In 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;
}
}
}
}
In XAML
<Label Text="Long Press Me!" effects:LongPressedEffect.Command="{Binding ShowAlertCommand}" effects:LongPressedEffect.CommandParameter="{Binding .}">
<Label.Effects>
<effects:LongPressedEffect />
</Label.Effects>
</Label>
自 Xamarin.Forms 2.4.0,事件 Pressed
and Released
are offered out of the box (see PR).
注意:为了实现对讲机效果,您可能需要使用 Device.BeginInvokeOnMainThread
(或通过 Prism 的 IDeviceService
)调用后续操作,以便调用 Released
事件,否则 UI 线程可能会被阻塞。
或者,您可以将事件处理程序声明为 async
和 await
您的调用,以保持 UI 线程未被占用。
为了拦截 Xamarin 上的 Pressed
和 Released
事件,我使用了 Effects
属性,如 this official Guide 中所述。
使用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
...
N.B Effects
不仅可用于各种组件 StackLayout
.
我希望我的事件在按钮 按下和释放 时触发,但我只能在 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 中一起工作。
终于得到了@Jason建议的解决方案。我们开始...
在 PCL 项目中创建 Xamarin.Forms.Button 的子 class,带有事件 处理能力
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"); };
希望这对某人有所帮助:)
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();
}
};
这也可以通过效果而不是完整的自定义渲染器来完成。请参阅此 post 以了解如何操作:
https://alexdunn.org/2017/12/27/xamarin-tip-xamarin-forms-long-press-effect/
以防 post 消失,这里是您可以实现的代码:
In shared project:
/// <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);
}
}
In 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;
}
}
}
}
In 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;
}
}
}
}
In XAML
<Label Text="Long Press Me!" effects:LongPressedEffect.Command="{Binding ShowAlertCommand}" effects:LongPressedEffect.CommandParameter="{Binding .}">
<Label.Effects>
<effects:LongPressedEffect />
</Label.Effects>
</Label>
自 Xamarin.Forms 2.4.0,事件 Pressed
and Released
are offered out of the box (see PR).
注意:为了实现对讲机效果,您可能需要使用 Device.BeginInvokeOnMainThread
(或通过 Prism 的 IDeviceService
)调用后续操作,以便调用 Released
事件,否则 UI 线程可能会被阻塞。
或者,您可以将事件处理程序声明为 async
和 await
您的调用,以保持 UI 线程未被占用。
为了拦截 Xamarin 上的 Pressed
和 Released
事件,我使用了 Effects
属性,如 this official Guide 中所述。
使用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
...
N.B Effects
不仅可用于各种组件 StackLayout
.