始终隐藏 Xamarin Forms 的键盘 Android 输入控件(条码扫描器输入)

Always hide keyboard for Xamarin Forms Android Entry Control (Barcode Scanner input)

我是 Xamarin.Forms 和移动应用程序开发的新手,非常感谢您的耐心和善意!正在使用 Xamarin.Forms PCL 构建条形码扫描器应用程序,尝试使用 MVVM。扫描仪是外部蓝牙设备(因此不能使用 ZXing)。

此项目有一个固定要求,即使用扫描仪作为键盘输入,并让用户能够快速将一个蓝牙设备换成另一个品牌(因此不能使用特定于设备的 API)。第二个要求是永远不允许用户直接在 Entry 控件中键入任何内容。输入应该来自扫描仪,而且只能来自扫描仪,因此我们不希望键盘显示在扫描页面上。

还有其他页面具有输入控件,用户需要在其中访问键盘,并且扫描仪应该能够保持与蓝牙的连接,即使在显示非扫描屏幕时也是如此。因此,我需要一种可靠的方法来将软键盘设置为从不在扫描页面上显示(此页面上只有一个输入控件,并且仅供扫描仪使用),但允许在其他设备上访问键盘页数。

当在扫描页面上时,我们希望始终将焦点设置在扫描仪的 Entry 控件上,因此当控件收到 Completed 事件时,我们对接收到的值进行处理,然后清除控件并重新设置专注于它,为下一次扫描做准备。

我一直在编写自定义控件和 android 渲染器,以及设置依赖项(首选),两者都取得了部分成功。无论哪种方式,都存在与将焦点设置在控件上的时间相关的时间问题。如果在设置焦点之前没有足够的延迟,软键盘将保持可见。在提供的代码示例中,我添加了一个短暂的睡眠延迟,主要用于隐藏键盘。但是,每次扫描时键盘仍然 "flashes" 短暂地出现在屏幕上,这看起来很糟糕。真的更喜欢一个不那么笨拙和丑陋的解决方案。

有没有一种好的、简单的方法可以为页面完全删除软键盘,同时仍然允许输入控件接收焦点,以便可以接收扫描的条形码? And/or 还有其他建议可以让我仍然满足要求吗?

(PS: 扫描页面目前没有使用MVVM绑定。只是想先让键盘消失,然后再进行绑定。)

下面是我尝试解决的一种方法。还有其他人。注意:最终我采用了一种完全不同的方法,我将 post 作为答案。

自定义控件(在PCL):

using Xamarin.Forms;

namespace MyPCL.Views
{
    //See ScanEntryRenderer in the Android project.
    public class ScanEntryControl : Entry
    {
        public ScanEntryControl() { }
    }
}

Xaml页面(注意自定义控件上的InputTransparent = "True"。这样用户就不能直接在android设备上输入。所有输入都必须来自蓝牙扫描仪).

<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             xmlns:local="clr-namespace:MyPCL.Views"
             x:Class="MyPCL.Views.ScanTestPage"
             Title="Scan Test Page" >
    <ContentPage.Content>
        <StackLayout>
            <Label Text="Scanner Test"  />
            <local:ScanEntryControl x:Name="BarcodeEntry" 
                Completed="BarcodeEntryCompleted" 
                InputTransparent="True"/>
            <Label x:Name="ResultLabel"  />
        </StackLayout>
    </ContentPage.Content>
</ContentPage>

表格背后的代码:

using System;
using Xamarin.Forms;
using Xamarin.Forms.Xaml;

namespace MyPCL.Views
{
    [XamlCompilation(XamlCompilationOptions.Compile)]
    public partial class ScanTestPage : ContentPage
    {
        public ScanTestPage()
        {
            InitializeComponent();
            BarcodeEntry.Focus();
        }

        protected override void OnAppearing()
        {
            base.OnAppearing();
            BarcodeEntry.Focus();
        }

        private void BarcodeEntryCompleted(object sender, EventArgs e)
        {
            if (!string.IsNullOrWhiteSpace(BarcodeEntry.Text))
            {
                ResultLabel.Text = "You entered: " + BarcodeEntry.Text;
                BarcodeEntry.Text = string.Empty;
            }
            BarcodeEntry.Focus();
        }
    }
}

Android 渲染器:

using Android.Content;
using Xamarin.Forms;
using MyPCL.Views;
using MyPCL.Droid;
using Xamarin.Forms.Platform.Android;
using Android.Views.InputMethods;

[assembly: ExportRenderer(typeof(ScanEntryControl), typeof(ScanEntryRenderer))]
namespace MyPCL.Droid
{
    public class ScanEntryRenderer : EntryRenderer
    {
        protected override void OnElementChanged(ElementChangedEventArgs<Entry> e)
        {
            base.OnElementChanged(e);
            if (e.NewElement != null)
            {
                ((ScanEntryControl)e.NewElement).PropertyChanging += OnPropertyChanging;
            }

            if (e.OldElement != null)
            {
                ((ScanEntryControl)e.OldElement).PropertyChanging -= OnPropertyChanging;
            }

            // Disable the Keyboard on Focus
            this.Control.ShowSoftInputOnFocus = false;
        }

        private void OnPropertyChanging(object sender, PropertyChangingEventArgs propertyChangingEventArgs)
        {
            // Check if the view is about to get Focus
            if (propertyChangingEventArgs.PropertyName == VisualElement.IsFocusedProperty.PropertyName)
            {
                // Dismiss the Keyboard 
                InputMethodManager imm = (InputMethodManager)this.Context.GetSystemService(Context.InputMethodService);
                imm.HideSoftInputFromWindow(this.Control.WindowToken, 0);
            }
        }
    }
}

I have been stumbling around writing custom controls and android renderers, and with setting up dependencies (preferred), both with partial success.

你可以在你的scanning page中使用EditText.ShowSoftInputOnFocus来实现,那么当你的输入获得焦点时键盘不会出现:

using Android.Content;
using Android.Views.InputMethods;
using Edi;
using Edi.Droid;
using Xamarin.Forms;
using Xamarin.Forms.Platform.Android;
[assembly: ExportRenderer(typeof(ScanEntryControl), typeof(ScanEntryRenderer))]
namespace Edi.Droid
{
    public class ScanEntryRenderer : EntryRenderer
    {
        protected override void OnElementChanged(ElementChangedEventArgs<Entry> e)
        {
            base.OnElementChanged(e);
            if (e.NewElement != null)
            {
                ((ScanEntryControl)e.NewElement).PropertyChanging += OnPropertyChanging;
            }

            if (e.OldElement != null)
            {
                ((ScanEntryControl)e.OldElement).PropertyChanging -= OnPropertyChanging;
            }

            // Disable the Keyboard on Focus
            this.Control.ShowSoftInputOnFocus = false;
        }

        private void OnPropertyChanging(object sender, PropertyChangingEventArgs propertyChangingEventArgs)
        {
            // Check if the view is about to get Focus
            if (propertyChangingEventArgs.PropertyName == VisualElement.IsFocusedProperty.PropertyName)
            {
                // incase if the focus was moved from another Entry
                // Forcefully dismiss the Keyboard 
                InputMethodManager imm = (InputMethodManager)this.Context.GetSystemService(Context.InputMethodService);
                imm.HideSoftInputFromWindow(this.Control.WindowToken, 0);
            }
        }
    }
}

在其他页面你仍然可以使用Entry,所以键盘会出现。

更新:

ScanEntryControl class 在 PCL:

using Xamarin.Forms;

namespace Edi
{
    public class ScanEntryControl : Entry
    {
    }
}

.xaml 文件:

<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             xmlns:local="clr-namespace:Edi"
             x:Class="Edi.MainPage">
    <ContentPage.Content>
        <StackLayout>
            <local:ScanEntryControl Text="ScanEntryControl"/>
            <Entry Text="Entry"/>
        </StackLayout>
    </ContentPage.Content>
</ContentPage>

这个答案没有直接解决原始问题,因为它不涉及 Entry 控件。然而,这是唯一对我有用的东西,最终成为一个更优雅的解决方案:

蓝牙扫描仪默认处于 HID 模式(人机界面设备),这意味着它与应用程序交互的唯一方式是模仿按键,因此需要一个 Entry (EditText) 控件或类似控件。我将扫描仪切换到 SPP 模式(串行端口配置文件)并修改了 this page (see also the GitHub repo here, and for more info on HID vs SPP see this document) 中的代码。

生成的代码激活扫描仪,然后 "listens" 进行输入。当接收到输入时,它显示在 Label 而不是 Entry 控件中。

Entry 控件还有其他一些我之前没有提到的问题:它通常会在条形码的前面添加一个重复字符 and/or 从末尾砍掉一个或多个字符。 SPP 解决方案也解决了所有这些问题。如果有人想要我想出的代码,请告诉我。将其组合成一个通用示例需要一些工作,因此暂时不发布。

我遇到了同样的问题。我在 Xamarin 论坛上找到了一个示例,恕我直言,它包含关键解决方案: 您必须覆盖 Focus() 并且不得调用基本方法。这使您可以完全控制虚拟键盘。在所有其他解决方案中,我看到有时会出现虚拟键盘。

当然,您的自定义条目需要 show/hide 键盘的方法。您可以在 OnFocus() 方法中调用它们。我的示例控件(见下文)也有一个可绑定的 属性,它允许您在 Focus 上自动显示虚拟键盘。因此,您可以为每个字段决定是否应自动显示键盘。 此外,我还包含了另一个对象,它会通知您虚拟键盘当前是否可见及其大小,以防您需要相应地调整布局大小。

由于这在几个不同的论坛中是一个很常见的问题,所以我决定创建一个示例控件和一个小应用程序来展示这些功能。 此外,我写了一个详细的自述文件,解释了实施的所有关键点。

你会在这里找到它:https://github.com/UweReisewitz/XamarinAndroidEntry

    protected override void OnAppearing()
    {
        base.OnAppearing();

        txtLotID.Focus();
    }

    private void OnLoad()
    {
        Init();

        swScanMode.IsToggled = Global.IsScannable;
        txtLotID.EnableKeyboard = !Global.IsScannable;
        txtLotID.OnEntryScanned += BtnSearch_Clicked;

        Device.StartTimer(TimeSpan.FromSeconds(1), () =>
        {
            if (txtLotID.IsReadOnly)
            {
                txtLotID.Text = "";
                **txtLotID.IsReadOnly = false;**
                txtLotID.GetFocus();
            }

            return true;
        });
    }