如何使用InputConnection实现WebView带逐字符延迟的模拟输入?
WebView模拟软键盘逐字符输入时内容被覆盖的问题解决
问题根源分析
在Android 12环境下,使用InputConnection的commitText方法逐字符输入文本时,出现后续字符清除之前内容的现象,核心原因有两点:
commitText参数理解偏差:commitText(CharSequence text, int newCursorPosition)的第二个参数newCursorPosition控制光标相对位置:- 传入
1时,WebView的InputConnection会将其解析为替换当前选中内容(而非追加),导致每次输入都会覆盖之前的文本; - 传入
0时,文本会追加到当前光标位置,且光标停在新输入文本开头,不会触发替换逻辑。
- 传入
- InputConnection实例错误复用:原代码中手动调用
onCreateInputConnection(EditorInfo())创建新的InputConnection,而非复用WebView与输入法关联的实例,导致每次输入都在独立上下文执行,加剧内容覆盖问题。
修正方案
修正后的代码
class CustomWebView(context: Context, attrs: AttributeSet): WebView(context, attrs) { private var inputConnection: InputConnection? = null override fun onCreateInputConnection(outAttrs: EditorInfo?): InputConnection? { val newInputConnection = super.onCreateInputConnection(outAttrs) ?: return inputConnection inputConnection = newInputConnection return newInputConnection } suspend fun sendTextInput(text: String, keyDelayStart: Long, keyDelayEnd: Long) { val imm = context.getSystemService(Context.INPUT_METHOD_SERVICE) as? InputMethodManager ?: throw Exception("could not get system's InputMethodManager") requestFocus() val defer = CompletableDeferred<Int>() // AsyncResultReceiver是自定义ResultReceiver,用于在回调中resolve deferred imm.showSoftInput(this, 0, AsyncResultReceiver(WeakReference(defer))) val result = defer.await() if (result == InputMethodManager.RESULT_HIDDEN || result == InputMethodManager.RESULT_UNCHANGED_HIDDEN) throw Exception("could not show IME keyboard") // 复用WebView关联的InputConnection实例 val ic = inputConnection ?: throw Exception("could not get valid InputConnection") for (char in text) { // 传入0确保文本追加,不触发替换 ic.commitText(char.toString(), 0) delay(Random.nextLong(keyDelayStart, keyDelayEnd)) } imm.hideSoftInputFromWindow(windowToken, 0) } }
关键修正点
- 复用InputConnection实例:直接使用WebView在输入法绑定过程中创建的
inputConnection,保证输入上下文一致,避免独立环境导致的内容覆盖。 - 设置
commitText参数为0:适配Android 12 WebView的InputConnection逻辑,确保文本以追加方式输入,而非替换现有内容。
进阶优化(光标停在文本末尾)
如果需要每次输入后光标停在文本末尾,可在提交字符前手动将光标移到现有文本末尾,再使用commitText参数1:
for (char in text) { // 获取当前文本长度,将光标移到末尾 val currentTextLength = ic.getTextBeforeCursor(Int.MAX_VALUE, 0).length ic.setSelection(currentTextLength, currentTextLength) // 提交字符,光标停在新字符末尾 ic.commitText(char.toString(), 1) delay(Random.nextLong(keyDelayStart, keyDelayEnd)) }
内容的提问来源于stack exchange,提问作者hldev
相关产品推荐
相关产品推荐

