React Native 0.68.2如何无需聚焦TextInput捕获物理扫码器输入?
实现 React Native 0.68.2 监听内置物理扫码器事件(无需可见 TextInput)
大部分内置手持物理扫码器会将扫描结果模拟为键盘输入流(快速输入一串字符,通常以换行符\n结尾)。基于这个特性,我们可以通过跨平台的监听方案实现需求,无需聚焦可见输入框:
Android 端:全局按键事件监听
Android 支持直接捕获全局按键事件,无需依赖输入框。可以借助react-native-keyevent库实现:
1. 依赖安装与配置
npm install react-native-keyevent --save
在android/app/src/main/java/[你的包名]/MainActivity.java中添加按键事件转发逻辑:
import com.github.kevinejohn.keyevent.KeyEventModule; import android.view.KeyEvent; @Override public boolean onKeyDown(int keyCode, KeyEvent event) { KeyEventModule.getInstance().onKeyDownEvent(keyCode, event); return super.onKeyDown(keyCode, event); }
2. RN 代码实现
import React, { useState, useEffect } from 'react'; import { View, Text, Platform } from 'react-native'; import KeyEvent from 'react-native-keyevent'; const ScannerHandler = () => { const [scannedResult, setScannedResult] = useState(''); let currentInput = ''; useEffect(() => { if (Platform.OS !== 'android') return; // 注册按键按下监听 KeyEvent.onKeyDownListener((event) => { const keyCode = event.keyCode; let char = ''; // 映射常见按键码到对应字符,可根据扫码器输出补充 const keyMap = { 7: '0', 8: '1', 9: '2', 10: '3', 11: '4', 12: '5', 13: '6', 14: '7', 15: '8', 16: '9', 29: 'a', 30: 'b', 31: 'c', 32: 'd', 33: 'e', 34: 'f', 35: 'g', 36: 'h', 37: 'i', 38: 'j', 39: 'k', 40: 'l', 41: 'm', 42: 'n', 43: 'o', 44: 'p', 45: 'q', 46: 'r', 47: 's', 48: 't', 49: 'u', 50: 'v', 51: 'w', 52: 'x', 53: 'y', 54: 'z', 56: '-', 57: '=', 61: ';', 63: ',', 64: '.' }; if (keyCode in keyMap) { char = keyMap[keyCode]; // 处理大写(Shift键按下时) if (event.metaState & KeyEvent.META_SHIFT_ON) { char = char.toUpperCase(); } } else if (keyCode === 66) { // 回车键(扫码器通常以此结尾) if (currentInput.length > 0) { setScannedResult(currentInput); currentInput = ''; } return; } if (char) currentInput += char; }); // 组件卸载时移除监听 return () => KeyEvent.removeKeyDownListener(); }, []); return ( <View> <Text>扫描结果:{scannedResult}</Text> </View> ); }; export default ScannerHandler;
iOS 端:隐藏 TextInput 捕获输入
iOS 不支持全局键盘事件监听,可通过隐藏的自动聚焦 TextInput捕获扫码输入:
import React, { useState, useRef } from 'react'; import { View, Text, TextInput, Platform } from 'react-native'; const ScannerHandler = () => { const [scannedResult, setScannedResult] = useState(''); const hiddenInputRef = useRef(null); const handleScanComplete = (text) => { const cleanText = text.replace('\n', '').trim(); if (cleanText) { setScannedResult(cleanText); hiddenInputRef.current?.clear(); } }; return ( <View> <Text>扫描结果:{scannedResult}</Text> {Platform.OS === 'ios' && ( <TextInput ref={hiddenInputRef} autoFocus={true} style={{ position: 'absolute', opacity: 0, height: 0, width: 0 }} onSubmitEditing={(e) => handleScanComplete(e.nativeEvent.text)} onChangeText={(text) => { if (text.includes('\n')) handleScanComplete(text); }} /> )} </View> ); }; export default ScannerHandler;
跨平台整合版本
将两端逻辑合并,实现一套代码兼容双平台:
import React, { useState, useEffect, useRef } from 'react'; import { View, Text, TextInput, Platform } from 'react-native'; import KeyEvent from 'react-native-keyevent'; const ScannerHandler = () => { const [scannedResult, setScannedResult] = useState(''); const hiddenInputRef = useRef(null); let currentAndroidInput = ''; // Android 按键监听 useEffect(() => { if (Platform.OS !== 'android') return; KeyEvent.onKeyDownListener((event) => { const keyCode = event.keyCode; let char = ''; const keyMap = { 7: '0', 8: '1', 9: '2', 10: '3', 11: '4', 12: '5', 13: '6', 14: '7', 15: '8', 16: '9', 29: 'a', 30: 'b', 31: 'c', 32: 'd', 33: 'e', 34: 'f', 35: 'g', 36: 'h', 37: 'i', 38: 'j', 39: 'k', 40: 'l', 41: 'm', 42: 'n', 43: 'o', 44: 'p', 45: 'q', 46: 'r', 47: 's', 48: 't', 49: 'u', 50: 'v', 51: 'w', 52: 'x', 53: 'y', 54: 'z', 56: '-', 57: '=', 61: ';', 63: ',', 64: '.' }; if (keyCode in keyMap) { char = keyMap[keyCode]; if (event.metaState & KeyEvent.META_SHIFT_ON) char = char.toUpperCase(); } else if (keyCode === 66) { if (currentAndroidInput.length > 0) { setScannedResult(currentAndroidInput); currentAndroidInput = ''; } return; } if (char) currentAndroidInput += char; }); return () => KeyEvent.removeKeyDownListener(); }, []); // iOS 输入处理 const handleIOSScan = (text) => { const cleanText = text.replace('\n', '').trim(); if (cleanText) { setScannedResult(cleanText); hiddenInputRef.current?.clear(); } }; return ( <View> <Text>扫描结果:{scannedResult}</Text> {Platform.OS === 'ios' && ( <TextInput ref={hiddenInputRef} autoFocus={true} style={{ position: 'absolute', opacity: 0, height: 0, width: 0 }} onSubmitEditing={(e) => handleIOSScan(e.nativeEvent.text)} onChangeText={(text) => text.includes('\n') && handleIOSScan(text)} /> )} </View> ); }; export default ScannerHandler;
注意事项
- 部分扫码器可配置输出后缀(如是否带换行符),建议设置为带换行符,便于判断扫码结束。
- Android 端的按键码映射需根据实际扫码器输出调整,可通过打印
event对象查看具体参数。 - iOS 端的隐藏输入框不会影响页面布局,
opacity:0和宽高0确保完全不可见。
内容的提问来源于stack exchange,提问作者holydragon
相关产品推荐
相关产品推荐

