InputMethodManager.SHOW_FORCED导致显示全局弹窗后输入法未自动隐藏

309 阅读2分钟

问题背景

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);