Android点击EditText外隐藏软键盘 点其他EditText不隐藏解决方法
问题根因
原有逻辑的缺陷非常明确:仅判断点击位置落在当前聚焦的EditText区域外时,就直接执行隐藏软键盘操作,没有校验点击的目标控件类型。当点击其他EditText时,虽然新控件会后续请求焦点,但已经提前触发了键盘隐藏,就会出现键盘闪一下或者直接收起的异常。
另外你为每个EditText单独设置的OnFocusChangeListener中手动调用clearFocus()属于冗余逻辑:焦点发生转移时,系统会自动清除原View的焦点状态,重复手动调用反而可能触发无意义的焦点回调,这部分代码可以直接删除。
修正逻辑
核心调整点:在判定点击位置在当前聚焦EditText外部后,不要立刻隐藏键盘,先查找点击坐标对应的实际目标控件:
- 如果目标控件是可输入的EditText类:仅清除当前输入框焦点,不执行键盘隐藏,等待新EditText自动请求焦点即可,键盘会保持显示状态
- 如果目标控件是非EditText类:清除当前焦点,同时执行软键盘隐藏操作
最终实现代码
首先删除所有EditText上冗余的setOnFocusChangeListener代码,然后在Activity中添加如下实现:
override fun dispatchTouchEvent(event: MotionEvent): Boolean { if (event.action == MotionEvent.ACTION_DOWN) { val currentFocusedView = currentFocus if (currentFocusedView is EditText) { val touchX = event.rawX.toInt() val touchY = event.rawY.toInt() val currentEditRect = Rect() currentFocusedView.getGlobalVisibleRect(currentEditRect) // 点击位置不在当前聚焦输入框范围内才做后续处理 if (!currentEditRect.contains(touchX, touchY)) { // 查找点击位置对应的实际控件 val touchedTarget = findTargetView(touchX, touchY) // 只有点击的不是输入框时,才需要隐藏键盘 val needHideKeyboard = touchedTarget !is EditText currentFocusedView.clearFocus() if (needHideKeyboard) { val imm = getSystemService(INPUT_METHOD_SERVICE) as InputMethodManager imm.hideSoftInputFromWindow(currentFocusedView.windowToken, 0) } } } } return super.dispatchTouchEvent(event) } /** * 根据屏幕坐标查找对应位置的可点击View */ private fun findTargetView(x: Int, y: Int): View? { val decorView = window.decorView val locationCache = intArrayOf(0, 0) return traverseViewHierarchy(decorView, x, y, locationCache) } private fun traverseViewHierarchy(view: View, x: Int, y: Int, locationCache: IntArray): View? { // 跳过不可见、不可交互的View if (view.visibility != View.VISIBLE || !view.isClickable) { return if (view is ViewGroup) { for (i in 0 until view.childCount) { val childResult = traverseViewHierarchy(view.getChildAt(i), x, y, locationCache) if (childResult != null) return childResult } null } else null } view.getLocationOnScreen(locationCache) val left = locationCache[0] val top = locationCache[1] val right = left + view.width val bottom = top + view.height // 坐标落在当前View范围内 if (x in left..right && y in top..bottom) { return if (view is ViewGroup) { // 优先返回子层级中命中的最上层View var childHit: View? = null for (i in 0 until view.childCount) { val res = traverseViewHierarchy(view.getChildAt(i), x, y, locationCache) if (res != null) { childHit = res break } } childHit ?: view } else view } return null }
适配注意点
如果项目中使用了自定义输入框控件(继承自EditText或AppCompatEditText的子类),上述逻辑可以自动识别;如果使用了基于TextView实现的自定义可输入组件,可以将判断条件替换为校验输入类型,避免漏判:
val needHideKeyboard = if (touchedTarget is TextView) { touchedTarget.inputType == InputType.TYPE_NULL } else true
内容的提问来源于stack exchange,提问作者sowinskidev
相关产品推荐
相关产品推荐

