如何在Xamarin Forms中隐藏模拟键盘读卡器的接收数据并存入变量
刚好做过类似的需求,咱们用Xamarin Forms的依赖服务来实现跨平台的读卡器数据捕获,全程不显示输入内容,直接存到变量里。具体步骤如下:
1. 定义共享接口(Xamarin Forms项目)
首先在共享代码层定义一个接口,统一跨平台的调用契约,这样咱们不用关心每个平台的具体实现:
public interface ICardReaderService { // 读到卡号后触发的事件 event EventHandler<string> CardReadCompleted; // 启动监听 void StartListening(); // 停止监听 void StopListening(); }
2. Android平台实现
Android这边咱们用一个完全隐藏的EditText来捕获读卡器的模拟键盘输入,既不需要额外权限,又能确保输入不会显示出来:
[assembly: Dependency(typeof(CardReaderService_Android))] namespace YourAppName.Droid { public class CardReaderService_Android : ICardReaderService { private EditText _hiddenInput; private string _currentInput = string.Empty; public event EventHandler<string> CardReadCompleted; public void StartListening() { var mainActivity = MainActivity.Instance; if (mainActivity == null || _hiddenInput != null) return; // 创建隐藏的输入框 _hiddenInput = new EditText(mainActivity) { Visibility = ViewStates.Invisible, Focusable = true, FocusableInTouchMode = true }; // 添加到当前页面的根布局 var rootLayout = mainActivity.FindViewById<ViewGroup>(Android.Resource.Id.Content); rootLayout.AddView(_hiddenInput); // 让输入框获取焦点,确保能捕获键盘输入 _hiddenInput.RequestFocus(); // 绑定监听事件 _hiddenInput.TextChanged += OnTextUpdated; _hiddenInput.KeyPress += OnKeyPressed; } private void OnTextUpdated(object sender, Android.Text.TextChangedEventArgs e) { _currentInput = _hiddenInput.Text; } private void OnKeyPressed(object sender, View.KeyEventArgs e) { // 捕获回车键(读卡器最后会触发回车) if (e.KeyCode == Keycode.Enter) { e.Handled = true; // 拦截回车事件,不让它扩散 CardReadCompleted?.Invoke(this, _currentInput.Trim()); // 清空输入框,准备下一次读取 _hiddenInput.Text = string.Empty; _currentInput = string.Empty; // 重新获取焦点,防止焦点丢失 _hiddenInput.RequestFocus(); } } public void StopListening() { if (_hiddenInput == null) return; var mainActivity = MainActivity.Instance; var rootLayout = mainActivity.FindViewById<ViewGroup>(Android.Resource.Id.Content); rootLayout.RemoveView(_hiddenInput); // 解绑事件,避免内存泄漏 _hiddenInput.TextChanged -= OnTextUpdated; _hiddenInput.KeyPress -= OnKeyPressed; _hiddenInput.Dispose(); _hiddenInput = null; } } }
注意:需要在
MainActivity里保存静态实例,方便服务调用:public class MainActivity : FormsAppCompatActivity { public static MainActivity Instance { get; private set; } protected override void OnCreate(Bundle savedInstanceState) { Instance = this; // ...其他初始化代码 } }
3. iOS平台实现
iOS这边同样用隐藏的UITextField来捕获输入,逻辑和Android基本一致:
[assembly: Dependency(typeof(CardReaderService_iOS))] namespace YourAppName.iOS { public class CardReaderService_iOS : ICardReaderService { private UITextField _hiddenInput; private string _currentInput = string.Empty; public event EventHandler<string> CardReadCompleted; public void StartListening() { if (_hiddenInput != null) return; _hiddenInput = new UITextField { Hidden = true, Enabled = true, UserInteractionEnabled = true }; // 添加到当前窗口 var window = UIApplication.SharedApplication.KeyWindow; window.AddSubview(_hiddenInput); // 成为第一响应者,捕获键盘输入 _hiddenInput.BecomeFirstResponder(); // 绑定监听事件 _hiddenInput.EditingChanged += OnTextUpdated; _hiddenInput.ShouldReturn += OnShouldReturn; } private void OnTextUpdated(object sender, EventArgs e) { _currentInput = _hiddenInput.Text; } private bool OnShouldReturn(UITextField textField) { // 捕获回车,触发完成事件 CardReadCompleted?.Invoke(this, _currentInput.Trim()); // 清空输入,保持响应状态 _hiddenInput.Text = string.Empty; _currentInput = string.Empty; _hiddenInput.BecomeFirstResponder(); return true; // 拦截默认回车行为 } public void StopListening() { if (_hiddenInput == null) return; _hiddenInput.ResignFirstResponder(); _hiddenInput.EditingChanged -= OnTextUpdated; _hiddenInput.ShouldReturn -= OnShouldReturn; _hiddenInput.RemoveFromSuperview(); _hiddenInput.Dispose(); _hiddenInput = null; } } }
4. 在Xamarin Forms中使用服务
现在回到共享代码层,咱们可以在App.xaml.cs或者页面里初始化服务,监听卡号读取事件,把数据存到变量里:
public partial class App : Application { private ICardReaderService _cardReaderService; // 存储读取到的卡号 public string LastReadCardNumber { get; private set; } public App() { InitializeComponent(); MainPage = new MainPage(); // 获取依赖服务实例 _cardReaderService = DependencyService.Get<ICardReaderService>(); if (_cardReaderService != null) { _cardReaderService.CardReadCompleted += CardReader_CardReadCompleted; _cardReaderService.StartListening(); } } private void CardReader_CardReadCompleted(object sender, string cardNumber) { // 把卡号存到变量里,这里可以做后续处理(比如存数据库、发起请求等) LastReadCardNumber = cardNumber; Console.WriteLine($"成功读取卡号:{cardNumber}"); } // 生命周期处理,避免后台浪费资源 protected override void OnSleep() { _cardReaderService?.StopListening(); } protected override void OnResume() { _cardReaderService?.StartListening(); } protected override void OnTerminate() { _cardReaderService?.StopListening(); } }
额外注意事项
- 有些读卡器会输出前缀/后缀字符,可以在
CardReadCompleted事件里对cardNumber做字符串过滤处理 - 确保隐藏输入控件始终保持焦点,比如页面切换后,可以在页面的
OnAppearing方法里重新调用服务的焦点设置逻辑(或者在服务里做自动焦点恢复) - 测试时可以用键盘手动输入数字+回车,模拟读卡器行为,验证功能是否正常
内容的提问来源于stack exchange,提问作者RDMM
相关产品推荐
相关产品推荐

