问题背景
Activity内有个EditText及删除按钮,点击删除时拉起输入法:
ImageView deleteAll;
EditText editText;
deleteAll.setOnClickListener(v -> {
editText.setText("");
final InputMethodManager manager = (InputMethodManager) editText.getContext().getSystemService(Context.INPUT_METHOD_SERVICE);
if (manager != null) {
manager.showSoftInput(editText, InputMethodManager.SHOW_FORCED);
}
});
在拉起输入法之后,恰好弹出一个全局弹窗:
Dialog dialog;
dialog.getWindow().setType(WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY);
dialog.show();
正常来说此时输入法会消失,但实际上并未消失。
问题原因分析
在window变化时,InputMethodManagerService#startInputOrWindowGainedFocus将会被调用,内部会调用startInputOrWindowGainedFocusInternalLocked方法:
private InputBindResult startInputOrWindowGainedFocusInternalLocked(/**省略部分参数列表**/int softInputMode) {
// 省略部分代码
switch (softInputMode & LayoutParams.SOFT_INPUT_MASK_STATE) {
case LayoutParams.SOFT_INPUT_STATE_UNSPECIFIED:
if (!sameWindowFocused && (!isTextEditor || !doAutoShow)) {
if (LayoutParams.mayUseInputMethod(windowFlags)) {
// There is no focus view, and this window will
// be behind any soft input window, so hide the
// soft input window if it is shown.
if (DEBUG) Slog.v(TAG, "Unspecified window will hide input");
hideCurrentInputLocked(
mCurFocusedWindow, InputMethodManager.HIDE_NOT_ALWAYS, null,
SoftInputShowHideReason.HIDE_UNSPECIFIED_WINDOW);
// 省略部分代码
break;
case LayoutParams.SOFT_INPUT_STATE_HIDDEN:
if ((softInputMode & LayoutParams.SOFT_INPUT_IS_FORWARD_NAVIGATION) != 0) {
if (DEBUG) Slog.v(TAG, "Window asks to hide input going forward");
hideCurrentInputLocked(mCurFocusedWindow, 0, null,
SoftInputShowHideReason.HIDE_STATE_HIDDEN_FORWARD_NAV);
}
break;
case LayoutParams.SOFT_INPUT_STATE_ALWAYS_HIDDEN:
if (!sameWindowFocused) {
if (DEBUG) Slog.v(TAG, "Window asks to hide input");
hideCurrentInputLocked(mCurFocusedWindow, 0, null,
SoftInputShowHideReason.HIDE_ALWAYS_HIDDEN_STATE);
}
break;
// 省略部分代码
}
上述代码会根据inputMode调用隐藏或者显示输入法,着重关注隐藏的方法hideCurrentInputLocked:
boolean hideCurrentInputLocked(IBinder windowToken, int flags, ResultReceiver resultReceiver, @SoftInputShowHideReason int reason) {
if ((flags & InputMethodManager.HIDE_IMPLICIT_ONLY) != 0 && (mShowExplicitlyRequested|| mShowForced)) {
if (DEBUG) Slog.v(TAG, "Not hiding: explicit show not cancelled by non-explicit hide");
return false;
}
if (mShowForced && (flags & InputMethodManager.HIDE_NOT_ALWAYS) != 0) {
if (DEBUG) Slog.v(TAG, "Not hiding: forced show not cancelled by not-always hide");
return false;
}
// 后面是真正隐藏输入法的逻辑
}
注意到若mShowForced为true,则不会执行隐藏输入法的逻辑,那么问题来了,此变量什么时候会为true?在showCurrentInputLocked方法里!
boolean showCurrentInputLocked(IBinder windowToken, int flags, ResultReceiver resultReceiver,
@SoftInputShowHideReason int reason) {
mShowRequested = true;
if (mAccessibilityRequestingNoSoftKeyboard || mImeHiddenByDisplayPolicy) {
return false;
}
if ((flags&InputMethodManager.SHOW_FORCED) != 0) {
mShowExplicitlyRequested = true;
mShowForced = true;
} else if ((flags&InputMethodManager.SHOW_IMPLICIT) == 0) {
mShowExplicitlyRequested = true;
}
// 省略部分代码
}
可以看出,实际上,此变量与显示输入法时传入的Flag有关,再看看如何调用的显示输入法:
manager.showSoftInput(editText, InputMethodManager.SHOW_FORCED);
问题原因总结
因显示输入法时传入的Flag为InputMethodManager.SHOW_FORCED,导致在Window切换时,隐藏输入法的代码未执行。
如何修复
换个Flag即可,如InputMethodManager.SHOW_IMPLICIT:
manager.showSoftInput(this, InputMethodManager.SHOW_IMPLICIT);