【问题标题】:Xamarin.Forms - Global tapped event on application levelXamarin.Forms - 应用程序级别的全局点击事件
【发布时间】:2019-05-03 06:21:00
【问题描述】:

如何在 Xamarin.Forms 应用程序中拦截应用程序级别的点击/单击事件?

我尝试在 MainPage 上使用类似的东西

    <Grid.GestureRecognizers>
        <TapGestureRecognizer Command="{Binding TappedCommand}" Tapped="TapGestureRecognizer_OnTapped"></TapGestureRecognizer>
    </Grid.GestureRecognizers>

但它只有在我点击空白区域时才有效,触摸按钮不会触发事件。

此外,我经常通过更改 MainPage 进行导航,因此即使它有效,我也必须添加这样的识别器。

我想在我的应用中全局处理任何触摸/点击/点击事件,是否可以在 Xamarin.Forms 中实现?

【问题讨论】:

  • 如果您希望元素向下传递输入事件而不是捕获它们,您可以在元素上设置 InputTransparent。我不知道拥有全局处理程序的方法 - 但您可以创建一个带有手势识别器的基页面类,您的所有页面都继承自该手势识别器。

标签: c# xamarin xamarin.forms xamarin.android


【解决方案1】:

这可以在特定于平台的应用程序中轻松设置,然后使用 Xamarin.Forms 依赖服务订阅/取消订阅事件。

您在这些事件中捕获的内容取决于您的需要,在此示例中,我只是捕获和事件 x/y 值。

一个通用的 EventArgs 类和 DP 接口:

public class TouchEventArgs<T> : EventArgs
{
    public T EventData { get; private set; }

    public TouchEventArgs(T EventData)
    {
        this.EventData = EventData;
    }
}

public interface IGlobalTouch
{
    void Subscribe(EventHandler handler);
    void Unsubscribe(EventHandler handler);
}

将以下内容添加到您的Xamarin.Android 应用程序的MainActivity

public EventHandler globalTouchHandler;
public override bool DispatchTouchEvent(MotionEvent ev)
{
    globalTouchHandler?.Invoke(null, new TouchEventArgs<Point>(new Point(ev.GetX(), ev.GetY())));
    return base.DispatchTouchEvent(ev);
}

Android 依赖实现:

public class GlobalTouch : IGlobalTouch
{
    public GlobalTouch() {}

    public void Subscribe(EventHandler handler)
    {
        (Forms.Context as MainActivity).globalTouchHandler += handler;
    }

    public void Unsubscribe(EventHandler handler)
    {
        (Forms.Context as MainActivity).globalTouchHandler -= handler;
    }
}

在 Xamarin.Forms 项目中的用法:

DependencyService.Get<IGlobalTouch>().Subscribe((sender, e) =>
{
    var point = (e as TouchEventArgs<Point>).EventData;
    System.Diagnostics.Debug.WriteLine($"{point.X}:{point.Y}");
});

注意:您应该分配一个代表以便退订...

输出:

307.628997802734:365.563842773438
309.280151367188:365.197265625
311.883605957031:365.390991210938
312.694641113281:380.148590087891
308.030578613281:387.823364257813
291.513244628906:396.339416503906
286.220489501953:396.339416503906
282.100006103516:396.339416503906

同样的技术也可以用于 iOS,有 TouchesBeganTouchesEndedTouchesMovedTouchesCancelled,您可以根据需要附加它们......

【讨论】:

  • 根据我的要求实施略有不同,但根据您的 sn-ps,工作就像一个魅力,谢谢! :)
  • @alekkowalczyk 很高兴它有帮助,编码愉快 ;-)
  • 这可行,但请注意,这些位置在较新的设备上取决于设备(具有 DPI),因此将结果除以 Context.Resources.DisplayMetrics.Density 以获得 DIP(设备独立像素)
  • 我的意思是密度而不是设备,Xam 点与触摸不匹配,请参见此处进行转换:stackoverflow.com/a/51422564/105028
  • 别忘了在 mainActivity 和 AppDelegate 中注册:Xamarin.Forms.DependencyService.Register();
【解决方案2】:

使用 SushiHangover 分辨率,对于 iOS,它会是这样的:

在 AppDelegate.cs 中实现 IUIGestureRecognizerDelegate

public partial class AppDelegate : global::Xamarin.Forms.Platform.iOS.FormsApplicationDelegate, IUIGestureRecognizerDelegate
{

在 AppDelegate.cs 类的 FinishedLaunching 方法中,添加:

Xamarin.Forms.DependencyService.Register<GlobalTouch>();
bool ret = base.FinishedLaunching(app, options);
if (ret)
{
    UITapGestureRecognizer tap = new UITapGestureRecognizer(Self, new ObjCRuntime.Selector("gestureRecognizer:shouldReceiveTouch:"));
    tap.Delegate = (IUIGestureRecognizerDelegate)Self;
    app.KeyWindow.AddGestureRecognizer(tap);
}
return ret;

在 AppDelegate.cs 中添加此方法:

[Export("gestureRecognizer:shouldReceiveTouch:")]
public bool ShouldReceiveTouch(UIGestureRecognizer gestureRecognizer, UITouch touch)
{
    Xamarin.Forms.DependencyService.Get<IGlobalTouch>().TapScreen();
    return false;
}

将此添加到 GlobalTouch:

EventHandler globalTouchHandler;

public void TapScreen()
{
    globalTouchHandler?.Invoke(this, null);
}

将此添加到 IGlobalTouch:

void TapScreen();

【讨论】:

  • 你是否也有像安卓一样返回位置和按下状态的工作版本?
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2023-04-03
  • 2015-04-30
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多