在应用程序级别上,是否可能钩取硬件键盘事件?

9
我正在开发一个适用于iOS、Android和UWP的Xamarin.Forms应用程序。我想要在应用程序级别(而不是单个页面级别,因为它是一个Xamarin.Forms应用程序)上钩入硬件键盘事件,以便当用户按下键盘上的键时,我可以在我的应用程序中调用一个动作。
在Android上,我使用MainActivity上的以下事件来实现这一点:。
public override bool OnKeyUp([GeneratedEnum] Keycode keyCode, KeyEvent e)
{
    //process key press
    return base.OnKeyUp(keyCode, e);
}

是否有与 Xamarin iOS 等效的解决方案?

4个回答

7
您可以使用KeyCommands来追踪硬件键盘按键。

keyCommands

支持硬件键盘命令的响应对象可以重新定义此属性,并使用它返回它支持的UIKeyCommand对象数组。每个键盘命令对象表示要识别的键盘序列和要调用的响应者中的操作方法。

您从此方法返回的键盘命令将应用于整个响应者链。当按下与键盘命令对象匹配的键组合时,UIKit会遍历响应者链寻找实现相应操作方法的对象。它在找到的第一个对象上调用该方法,然后停止处理事件。

在Xamarin.forms中,您需要为iOS平台的ContentPage创建自定义渲染器。然后在该页面渲染器中添加keycommands。

如果您想让ViewController(页面)处理按键而不只是输入控件(例如Entry),请使用canBecomeFirstResponder启用viewcontroller成为第一响应者。

例如,在iOS平台上,ContentPage的自定义渲染程序可能如下所示:

using System;
using Foundation;
using UIKit;
using Xamarin.Forms;
using Xamarin.Forms.Platform.iOS;
using KeyCommandsInXamarinForms.iOS;

[assembly: ExportRenderer(typeof(ContentPage), typeof(MyCustomPageRenderer))]
namespace KeyCommandsInXamarinForms.iOS
{
    public class MyCustomPageRenderer : PageRenderer
    {
        protected override void OnElementChanged(VisualElementChangedEventArgs e)
        {
            base.OnElementChanged(e);

            if (e.OldElement != null || Element == null)
            {
                return;
            }

            //Create your keycommand as you need.
            UIKeyCommand keyCommand1 = UIKeyCommand.Create(new NSString("1"), UIKeyModifierFlags.Command, new ObjCRuntime.Selector("Action:"));
            UIKeyCommand keyCommand2 = UIKeyCommand.Create(new NSString("\t"), 0, new ObjCRuntime.Selector("Action:"));
            //Add your keycommands
            this.AddKeyCommand(keyCommand1);
            this.AddKeyCommand(keyCommand2);
        }

        [Export("Action:")]
        private void Excute(UIKeyCommand keyCommand)
        {
            Console.WriteLine(String.Format("key pressed - {0}", keyCommand.Value);
        }


        //Enable viewcontroller to become the first responder, so it is able to respond to the key commands.
        public override bool CanBecomeFirstResponder
        {
            get
            {
                return true;
            }
        }
    }
}

请注意这一行,使用typeof(ContentPage)作为处理程序参数,那么您就不需要在您的PCL中做任何更改: [assembly: ExportRenderer(typeof(ContentPage), typeof(MyCustomPageRenderer))]

1
@user1,如果我的回答有帮助,请您接受它吧?这对我很重要! - Kevin Li
1
我已经尝试过这种方法,但在您的示例中,您没有将“Key”或“String”作为参数传递。使用这种技术是否可以传递参数?因为我需要知道按下了哪个按钮。 - JKennedy
注意:在当前的Xamarin Forms中,我不得不用ElementChangedEventArgs<Page>替换VisualElementChangedEventArgs - ToolmakerSteve

3

我需要在我的XamForms应用程序中允许数字输入和末尾的回车。以下是我在Xamarin Forms 3.0.0.482510 / Xamarin.iOS 11.10中解决它的方法(与上面Kevin的答案有点不同,因为我想在XamForms共享的xaml项目中处理它,而不是在iOS项目中):

在你的iOS项目中(例如,一个名为“MobileProject”的Xamarin Forms共享项目),创建一个新类:

using Foundation;
using UIKit;
using Xamarin.Forms;
using Xamarin.Forms.Platform.iOS;

[assembly: ExportRenderer(typeof(MobileProject.MainPage), typeof (com.YourCompany.iOS.KeyboardHookRenderer))]
namespace com.YourCompany.iOS
{
public class KeyboardHookRenderer : PageRenderer
{
    private string _RecvValue = string.Empty;

    public override bool CanBecomeFirstResponder
    {
        get { return true; }
    }

    protected override void OnElementChanged(VisualElementChangedEventArgs e)
    {
        base.OnElementChanged(e);
        string key = string.Empty;
        var selector = new ObjCRuntime.Selector("KeyRecv:");
        UIKeyCommand accelerator1 = UIKeyCommand.Create((NSString)"1", 0, selector);
        AddKeyCommand(accelerator1);
        UIKeyCommand accelerator2 = UIKeyCommand.Create((NSString)"2", 0, selector);
        AddKeyCommand(accelerator2);
        UIKeyCommand accelerator3 = UIKeyCommand.Create((NSString)"3", 0, selector);
        AddKeyCommand(accelerator3);

        ... etc as many as you need or use a loop based on key id...
    }

    [Export("KeyRecv:")]
    public void KeyRecv(UIKeyCommand cmd)
    {
        if (cmd == null)
            return;
        var inputValue = cmd.Input;
        if (inputValue == "\n" || inputValue == "\r")
        {
            ((MobileProject.MainPage) Element)?.HandleHardwareKeyboard(_RecvValue);
            _RecvValue = string.Empty;
        }
        else
        {
            _RecvValue += inputValue;
        }
    }
}
}

然后,在您的共享应用程序中的MainPage.xaml.cs文件中,您只需要添加以下代码:
/// <summary>
/// Handle hardware keys (from KeyboardHookRender.cs in iOS project)
/// </summary>
/// <param name="keys">Keys sent, including trailing Cr or Lf</param>
public void HandleHardwareKeyboard(string keys)
{
    SomeTextbox.Text = keys;
    // Whatever else you need to do to handle it
}

1
iOS的问题在于它没有这样的方法。首先,您需要了解要捕获哪些按钮。如果您想捕获音量按钮,则可以在此处找到答案: https://forums.xamarin.com/discussion/29648/detect-volume-change-from-hardware-buttons
public override void ViewDidLoad ()
    {
        base.ViewDidLoad ();

        //KVO for outputVolume
        var session = AVAudioSession.SharedInstance();
        var errorOrNull = session.SetActive (true);
        if (errorOrNull != null) {
            //TODO: ...handle it
        }
        session.AddObserver (this, "outputVolume", NSKeyValueObservingOptions.New, IntPtr.Zero);


    }

    public override void ObserveValue (NSString keyPath, NSObject ofObject, NSDictionary change, IntPtr context)
    {
                   //TODO: Filter as appropriate, error-handling, etc.
        var volume = (float) (change ["new"] as NSNumber);

        volumeLabel.Text = volume.ToString();
    }

如果您想捕获键盘事件,您需要订阅一个负责输入过程的视图。

您可以使用NotificationCenter订阅所有能够处理输入的视图,并在一个地方接收通知。

NSNotificationCenter.DefaultCenter.AddObserver (UITextField.TextFieldTextDidChangeNotification, (notification) =>
{
    Console.WriteLine ("Character received! {0}", notification.Object == TextField);
});

1

I did this way: in MainActivity.cs:

public override bool OnKeyUp([GeneratedEnum] Keycode keyCode, KeyEvent e)
    {
        if (keyCode == Keycode.VolumeDown)
        {
            var vm = MainViewModel.GetInstance().YourPageName;
            if (vm != null && App.Navigator.CurrentPage.GetType().Name == "YourPagePage")
            {
                vm.GuardarRecorrido();
                return true;
            }
        }

        return base.OnKeyUp(keyCode, e);
    }

我希望您能将音量键限制在该页面中运行,以便更好地控制。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接