You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在不显示虚拟键盘的前提下从输入法获取非键盘字符?

问题:避免硬件键盘触发虚拟键盘弹出

我正在为运行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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.24 19:27:11