InputMethodManager处理软键盘显示和隐藏的问题

本文介绍Android中通过InputMethodManager实现软键盘的可靠显示与隐藏方法,包括不同标志位的作用及使用0标志位的特殊效果。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

InputMethodManager处理软键盘显示和隐藏的问题

Android中对软键盘的处理,可以使用showSoftInput方法进行显示操作:

public boolean showSoftInput(View view, int flags)

而对应的隐藏方法为hideSoftInputFromWindow:

public boolean hideSoftInputFromWindow(IBinder windowToken, int flags)

其中对于显示和隐藏,都有一个关键的控制参数flags。

showSoftInput(View, int)标志
1、SHOW_FORCED:表示用户强制打开输入法(如长按菜单键),一直保持打开直至只有显式关闭。

2、SHOW_IMPLICIT:表示隐式显示输入窗口,非用户直接要求。窗口可能不显示。
hideSoftInputFromWindow(IBinder, int)标志
1、HIDE_IMPLICIT_ONLY:表示如果用户未显式地显示软键盘窗口,则隐藏窗口。

2、HIDE_NOT_ALWAYS:表示软键盘窗口总是隐藏,除非开始时以SHOW_FORCED显示。
神秘标志:0

在处理显示和隐藏发过程中flags可以取值0,但是这个标志没有明确说明,其跟另外几个标志在控制显示和隐藏的关系如下:

隐藏\显示0SHOW_IMPLICITSHOW_FORCED<-显示参数
0YYY
HIDE_IMPLICIT_ONLYNYN
HIDE_NOT_ALWAYSYYN

Y表示可以隐藏软键盘,N表示不可以隐藏软键盘

线索

InputMethodManager.java中showXXX方法和hideXXX方法最终调用的是InputMethodManagerService.java中的方法,其中show方法如下:

public boolean showSoftInput(IInputMethodClient client, int flags, ResultReceiver resultReceiver) {
	...
    return showCurrentInputLocked(flags, resultReceiver);   
}

而关键flags的处理在showCurrentInputLocked中的片段如下:

boolean showCurrentInputLocked(int flags, ResultReceiver resultReceiver) {
    ...
    if ((flags&InputMethodManager.SHOW_FORCED) != 0) {
        mShowExplicitlyRequested = true;
        mShowForced = true;
    } else if ((flags&InputMethodManager.SHOW_IMPLICIT) == 0) {
        mShowExplicitlyRequested = true;
    }

(该方法除此之外并没有其他地方处理flags)这说明flags并不影响显示,只是在显示方法中埋个雷以便于隐藏时需要。

接着再看到隐藏方法,最终调用的是hideCurrentInputLocked:

 boolean hideCurrentInputLocked(int flags, ResultReceiver resultReceiver) {
    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;
    }

    // There is a chance that IMM#hideSoftInput() is called in a transient state where
    // IMMS#InputShown is already updated to be true whereas IMMS#mImeWindowVis is still waiting
    // to be updated with the new value sent from IME process.  Even in such a transient state
    // historically we have accepted an incoming call of IMM#hideSoftInput() from the
    // application process as a valid request, and have even promised such a behavior with CTS
    // since Android Eclair.  That's why we need to accept IMM#hideSoftInput() even when only
    // IMMS#InputShown indicates that the software keyboard is shown.
    // TODO: Clean up, IMMS#mInputShown, IMMS#mImeWindowVis and mShowRequested.
    final boolean shouldHideSoftInput = (mCurMethod != null) && (mInputShown ||
            (mImeWindowVis & InputMethodService.IME_ACTIVE) != 0);
    boolean res;
    if (shouldHideSoftInput) {
        // The IME will report its visible state again after the following message finally
        // delivered to the IME process as an IPC.  Hence the inconsistency between
        // IMMS#mInputShown and IMMS#mImeWindowVis should be resolved spontaneously in
        // the final state.
        executeOrSendMessage(mCurMethod, mCaller.obtainMessageOO(
                MSG_HIDE_SOFT_INPUT, mCurMethod, resultReceiver));
        res = true;
    } else {
        res = false;
    }
    if (mHaveConnection && mVisibleBound) {
        mContext.unbindService(mVisibleConnection);
        mVisibleBound = false;
    }
    mInputShown = false;
    mShowRequested = false;
    mShowExplicitlyRequested = false;
    mShowForced = false;
    return res;
}

可以看到在隐藏方法中对flags的处理如下:

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

变量对应关系如下:

SHOW_FORCED   -> mShowExplicitlyRequested/mShowForced -> 0才不会被拦截:可以隐藏
0             -> mShowExplicitlyRequested             -> 0和SHOW_FORCED不会被拦截 :可以隐藏
SHOW_IMPLICIT -> 无                                   -> 都不拦截:可以隐藏

这也正好说明了在显示和隐藏过程中使用0的厉害之处。

综上,可以得到显示和隐藏的可靠方式为:

InputMethodManager imm =(InputMethodManager) context.getSystemService(Context.INPUT_METHOD_SERVICE);

显示:
imm.showSoftInput(view,0);

隐藏:
imm.hideSoftInputFromWindow(view.getWindowToken(),0); 

附:检测软键盘是否显示的可靠方法

public static boolean isSoftShowing(Activity a) {
    if (null == a) {
        return false;
    }
    Window w = a.getWindow();
    //获取当前屏幕内容的高度
    int screenHeight = w.getDecorView().getHeight();
    //获取View可见区域的bottom(键盘弹出时,除了键盘的其他区域)
    Rect rect = new Rect();
    w.getDecorView().getWindowVisibleDisplayFrame(rect);
    Display d = a.getWindowManager().getDefaultDisplay();
    DisplayMetrics metrics = new DisplayMetrics();
    //这个方法获取可能不是真实屏幕的高度
    d.getMetrics(metrics);
    int usableHeight = metrics.heightPixels;
    //获取当前屏幕的真实高度
    d.getRealMetrics(metrics);
    int realHeight = metrics.heightPixels;
    int barHeight = realHeight > usableHeight ? realHeight - usableHeight : 0;
    int leftSpace = screenHeight - rect.bottom - barHeight;
    return leftSpace != 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值