如何在不显示虚拟键盘的前提下从输入法获取非键盘字符?
问题:避免硬件键盘触发虚拟键盘弹出
我正在为运行Android系统的Datalogic手持条码扫描器开发应用。应用既可以通过API获取条码,也能利用扫描器的隐形输入法模拟键盘功能。我更倾向于第二种通用方案,这样应用就能在任何支持键盘模拟的扫描器,甚至是只有物理键盘的设备上运行。
在键盘模拟模式下:
- 常规键盘字符会通过视图的
onKeyDown/onKeyUp方法以键盘事件形式发送 - 特殊字符(比如GS1条码中的{FNC1}分组分隔符,编码\x1D)则会通过视图
InputConnection的commitText()方法发送
举个例子,内容为"10ABC{FNC1}21DEF"的GS1条码:
- "10ABC"通过
onKeyDown()/onKeyUp()发送 - {FNC1}通过
commitText()发送 - "21DEF"通过
onKeyDown()/onKeyUp()发送
我的问题是:当使用InputConnection时,在有硬件键盘但无隐形输入法的设备(比如Android Studio模拟器)上,按下硬件键会弹出虚拟键盘,怎么才能避免这种情况?
我的条码扫描界面结构很简单:一个获焦的LinearLayout,加上几个用于显示扫描结果的未获焦TextView,简化后的代码如下:
package com.example.myapplication import android.content.Context import androidx.appcompat.app.AppCompatActivity import android.os.Bundle import android.text.InputType import android.util.AttributeSet import android.view.KeyEvent import android.view.ViewGroup import android.view.ViewGroup.LayoutParams.MATCH_PARENT import android.view.ViewGroup.LayoutParams.WRAP_CONTENT import android.view.inputmethod.BaseInputConnection import android.view.inputmethod.EditorInfo import android.view.inputmethod.InputConnection import android.widget.LinearLayout import android.widget.TextView class MainActivity : AppCompatActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) val myLinearLayout = MyLinearLayout(this) myLinearLayout.layoutParams = ViewGroup.LayoutParams(MATCH_PARENT, MATCH_PARENT) myLinearLayout.orientation = LinearLayout.VERTICAL myLinearLayout.isFocusableInTouchMode = true myLinearLayout.requestFocus() val textView = TextView(this) textView.layoutParams = ViewGroup.LayoutParams(MATCH_PARENT, WRAP_CONTENT) myLinearLayout.addView(textView) setContentView(myLinearLayout) } } class MyLinearLayout @JvmOverloads constructor(context: Context, attributeSet: AttributeSet? = null, defStyleAttr: Int = 0, defStyleRes: Int = 0) : LinearLayout(context, attributeSet, defStyleAttr, defStyleRes) { fun appendText(text: String) { val textView = getChildAt(0) as TextView textView.text = textView.text.toString() + text } override fun onKeyDown(keyCode: Int, event: KeyEvent): Boolean { if (event.unicodeChar != 0) { appendText(event.unicodeChar.toChar().toString()) return true } return super.onKeyDown(keyCode, event) } override fun onCreateInputConnection(outAttrs: EditorInfo?): InputConnection { outAttrs?.inputType = InputType.TYPE_TEXT_VARIATION_VISIBLE_PASSWORD return MyInputConnection(this, false) } } class MyInputConnection(private val targetView: MyLinearLayout, fullEditor: Boolean) : BaseInputConnection(targetView, fullEditor) { override fun commitText(text: CharSequence?, newCursorPosition: Int): Boolean { targetView.appendText("\ncommitText: " + text.toString() + "\n") return true } }
解决方案
1. 修正InputConnection的配置
系统弹出虚拟键盘的核心原因是,当前视图被标记为需要文本输入的编辑器。修改onCreateInputConnection方法,明确告诉系统不需要软键盘:
override fun onCreateInputConnection(outAttrs: EditorInfo?): InputConnection { outAttrs?.run { // 设置为TYPE_NULL,表明不需要软键盘输入 inputType = InputType.TYPE_NULL // 添加标志,禁止全屏IME和空操作 imeOptions = EditorInfo.FLAG_NO_FULLSCREEN or EditorInfo.FLAG_NULL_IME_ACTION } return MyInputConnection(this, false) }
2. 重写onCheckIsTextEditor方法
默认情况下,实现了InputConnection的视图会被系统判定为文本编辑器,重写该方法返回false,直接否定这一判定:
override fun onCheckIsTextEditor(): Boolean { return false }
3. 强制隐藏软键盘(可选)
如果上述方法仍有残留问题,可以在视图初始化时添加触摸监听,确保软键盘不会弹出:
在MainActivity的onCreate中添加:
import android.view.inputmethod.InputMethodManager // ... myLinearLayout.setOnTouchListener { _, _ -> val imm = getSystemService(Context.INPUT_METHOD_SERVICE) as InputMethodManager imm.hideSoftInputFromWindow(myLinearLayout.windowToken, 0) false }
完整修改后的MyLinearLayout类
class MyLinearLayout @JvmOverloads constructor(context: Context, attributeSet: AttributeSet? = null, defStyleAttr: Int = 0, defStyleRes: Int = 0) : LinearLayout(context, attributeSet, defStyleAttr, defStyleRes) { fun appendText(text: String) { val textView = getChildAt(0) as TextView textView.text = textView.text.toString() + text } override fun onKeyDown(keyCode: Int, event: KeyEvent): Boolean { if (event.unicodeChar != 0) { appendText(event.unicodeChar.toChar().toString()) return true } return super.onKeyDown(keyCode, event) } override fun onCreateInputConnection(outAttrs: EditorInfo?): InputConnection { outAttrs?.run { inputType = InputType.TYPE_NULL imeOptions = EditorInfo.FLAG_NO_FULLSCREEN or EditorInfo.FLAG_NULL_IME_ACTION } return MyInputConnection(this, false) } override fun onCheckIsTextEditor(): Boolean { return false } }
内容的提问来源于stack exchange,提问作者Andrew Usachov
相关产品推荐
相关产品推荐

