如何检测Android设备上的软键盘是否可见?

331

在Android中,有没有一种方法可以检测软键盘是否显示在屏幕上?


2
可能是重复的问题:Android:如何判断软键盘是否显示? - Heath Hunnicutt
1
在某些情况下(如果安装了第三方键盘),解决方案可以是检查全局通知,因为当键盘打开时,会有一个系统通知显示“更改键盘”。这可以通过NotificationListenerService来完成。 - Prof
3
将近8年了,仍然没有可靠的解决方案。哦,如果他们介绍一个解决方案,它也只能适用于API > 30,所以没关系。 - M.kazem Akhgary
可能是Android:检测软键盘打开的重复问题。 - AbdelHady
如果你正在使用Espresso或Jetpack Compose进行UI测试,你可以使用executeShellCommand()来检测键盘是否显示在屏幕上:https://stackoverflow.com/questions/33970956/test-if-soft-keyboard-is-visible-using-espresso#53118977 - undefined
39个回答

7

现在基于 Kotlin 的 Android R 终于有了一种直接的方式。

 val imeInsets = requireView().rootWindowInsets.isVisible(WindowsInsetsCompat.Type.ime()) 
    if (imeInsets) { 
     //Ime is visible
     //Lets move our view by the height of the IME
     view.translationX = imeInsets.bottom }

无法解析“WindowsInsetsCompat”。 - Shoaib Kakal
@ShoaibKhalid 或许需要 androidx.core:core-ktx:1.9.0 - anhtuannd

7

终于在2023年,官方现在提供了对此的支持!

这里是文档

要检查键盘是否可见,请执行以下操作:

val insets = ViewCompat.getRootWindowInsets(view) ?: return
val imeVisible = insets.isVisible(WindowInsetsCompat.Type.ime())
val imeHeight = insets.getInsets(WindowInsetsCompat.Type.ime()).bottom

要监听键盘可见性的变化,请执行以下操作:

ViewCompat.setOnApplyWindowInsetsListener(view) { _, insets ->
  val imeVisible = insets.isVisible(WindowInsetsCompat.Type.ime())
  val imeHeight = insets.getInsets(WindowInsetsCompat.Type.ime()).bottom
  insets
}

注意:Google建议您配置应用程序以边缘到边缘显示,以便此功能正常工作。他们还说:“为了实现与此AndroidX实现的最佳向后兼容性,请在AndroidManifest.xml中将android:windowSoftInputMode="adjustResize"设置为活动。”

只有在通过WindowCompat.setDecorFitsSystemWindows(window, false)启用了边缘到边缘功能时,这才有效。然而,这会导致状态栏和导航栏重叠在您的布局上。解决这个问题的快速方法是将您的布局包装在一个FrameLayout中,并为您的布局的原始根视图设置android:fitsSystemWindows="true" - undefined

6
这对我所需的要求来说不太复杂。希望这能帮到您:
在MainActivity中:
public void dismissKeyboard(){
    InputMethodManager imm =(InputMethodManager)this.getSystemService(Context.INPUT_METHOD_SERVICE);
    imm.hideSoftInputFromWindow(mSearchBox.getWindowToken(), 0);
    mKeyboardStatus = false;
}

public void showKeyboard(){
    InputMethodManager imm =(InputMethodManager)this.getSystemService(Context.INPUT_METHOD_SERVICE);
    imm.toggleSoftInput(InputMethodManager.SHOW_FORCED, InputMethodManager.HIDE_IMPLICIT_ONLY);
    mKeyboardStatus = true;
}

private boolean isKeyboardActive(){
    return mKeyboardStatus;
}

默认情况下,mKeyboardStatus的基本布尔值将初始化为false

然后按以下方式检查该值,并在必要时执行操作:

 mSearchBox.requestFocus();
    if(!isKeyboardActive()){
        showKeyboard();
    }else{
        dismissKeyboard();
    }

这种情况下最简单的解决方案 ;) - dokam_scotland

6

如果您需要检查键盘状态,则应该可以使用以下方法:

fun Activity.isKeyboardOpened(): Boolean {
    val r = Rect()

    val activityRoot = getActivityRoot()
    val visibleThreshold = dip(UiUtils.KEYBOARD_VISIBLE_THRESHOLD_DP)

    activityRoot.getWindowVisibleDisplayFrame(r)

    val heightDiff = activityRoot.rootView.height - r.height()

    return heightDiff > visibleThreshold;
}

fun Activity.getActivityRoot(): View {
    return (findViewById<ViewGroup>(android.R.id.content)).getChildAt(0);
}

UiUtils.KEYBOARD_VISIBLE_THRESHOLD_DP = 100 且 dip() 是将 dp 转换为 px 的 Anko 函数时:

fun dip(value: Int): Int {
    return (value * Resources.getSystem().displayMetrics.density).toInt()
}

2
谢谢!你救了我的时间 =) - Nurlan Nabiyev
这个解决方法只有在清单文件中的活动具有以下属性时才有效。 android:windowSoftInputMode="adjustResize" 或 android:windowSoftInputMode="adjustPan" 它不适用于以下情况 android:windowSoftInputMode="adjustNothing" - Muhammad Maqsood
找不到UiUtils - undefined

5
你可以参考这个答案 - https://dev59.com/V2035IYBdhLWcg3wYO51#24105062,每次都对我有用。
注:该答案是关于IT技术的。
adb shell dumpsys window InputMethod | grep "mHasSurface"

如果软键盘可见,它将返回true。

12
这仅在开发过程中有用 - 不适合在应用程序中使用的解决方案。(用户不会运行adb。) - ToolmakerSteve

3

试试这段代码,如果键盘已经弹出,则此函数将返回true值。

private final String TAG = "TextEditor";
private TextView mTextEditor;

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_editor);
    mTextEditor = (TextView) findViewById(R.id.text_editor);
    mTextEditor.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
        @Override
        public void onGlobalLayout() {
            isKeyboardShown(mTextEditor.getRootView());
        }
    });
}

private boolean isKeyboardShown(View rootView) {
    /* 128dp = 32dp * 4, minimum button height 32dp and generic 4 rows soft keyboard */
    final int SOFT_KEYBOARD_HEIGHT_DP_THRESHOLD = 128;

    Rect r = new Rect();
    rootView.getWindowVisibleDisplayFrame(r);
    DisplayMetrics dm = rootView.getResources().getDisplayMetrics();
    /* heightDiff = rootView height - status bar height (r.top) - visible frame height (r.bottom - r.top) */
    int heightDiff = rootView.getBottom() - r.bottom;
    /* Threshold size: dp to pixels, multiply with display density */
    boolean isKeyboardShown = heightDiff > SOFT_KEYBOARD_HEIGHT_DP_THRESHOLD * dm.density;

    Log.d(TAG, "isKeyboardShown ? " + isKeyboardShown + ", heightDiff:" + heightDiff + ", density:" + dm.density
            + "root view height:" + rootView.getHeight() + ", rect:" + r);

    return isKeyboardShown;
}

当 isKeyboardShown 没有显示时,它会不断地调用自身。 - Mandeep Singh

3
private fun isKeyboardVisible(rootView: View) =
    ViewCompat.getRootWindowInsets(rootView)!!.isVisible(WindowInsetsCompat.Type.ime())

你需要一个非常好的理由来回答这样一个旧问题,因为已经有很多人回答过了。新的回答如果没有解释如何补充其他答案,往往会在审核过程中被删除。 - Gert Arnold
如果这个-1是你给的,那么至少解释一下答案有什么问题会更专业。 - Piotr Aleksander Chmielowski
我没有这样做吗?对于所有答案,解释它为什么这样做(并且是最好的方式)总是受欢迎的,但是对于旧问题的新答案也应该解释它们如何改进其他答案。你不想知道有多少人只是在没有查看现有答案的情况下发布答案,因此实际上只会添加噪音。无论哪种方式,仅包含代码的答案始终被认为是低质量答案,并且其中许多答案将被社区管理删除。 - Gert Arnold

3

如您所知,Android软键盘只会在有可能进行输入的事件发生时才会显示。换句话说,只有当EditText获得焦点时,键盘才会显示出来。这意味着您可以通过使用OnFocusChangeListener来判断键盘是否可见。

//Declare this Globally

public boolean isKeyBoardVisible = false;

//In OnCreate *[For Activity]*, OnCreateView *[For Fragment]*

text_send.setOnFocusChangeListener(new View.OnFocusChangeListener() {

    @Override
    public void onFocusChange(View v, boolean hasFocus) {
        if(hasFocus)
            isKeyBoardVisible = true;
        else
            isKeyBoardVisible = false;
    }
});

现在您可以在类的任何地方使用isKeyBoardVisible变量来获取键盘是否打开。这对我很有效。

注意:当使用InputMethodManager以编程方式打开键盘时,此过程不起作用,因为它不会调用OnFocusChangeListener。


并不是真正的黑客技巧,在嵌套片段的情况下无法工作。至于活动,我还没有尝试过。 - Antroid

3
我通过设置GlobalLayoutListener来实现这一点,具体操作如下:
final View activityRootView = findViewById(R.id.activityRoot);
activityRootView.getViewTreeObserver().addOnGlobalLayoutListener(
        new OnGlobalLayoutListener() {
            @Override
            public void onGlobalLayout() {
                int heightView = activityRootView.getHeight();
                int widthView = activityRootView.getWidth();
                if (1.0 * widthView / heightView > 3) {
                    //Make changes for Keyboard not visible
                } else {
                    //Make changes for keyboard visible
                }
            }
        });

这将会被非常频繁地调用。 - Denys Kniazhev-Support Ukraine
在哪些情况下,这与@BrownsooHan的答案不同?我正在寻找一种方法,使一个覆盖其他应用程序的应用程序可以避开键盘。 - Evan Langlois
他的回答基本上和我的一样,只不过我比他早几个月回答了这个问题,而且他得到了更多的赞同。 - PearsonArtPhoto

3

感谢大家的回答,我根据自己的情况找到了答案。

/**
 * Add global layout listener to observe system keyboard visibility
 */
private void initObserverForSystemKeyboardVisibility() {
    getRootView().getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
        @Override
        public void onGlobalLayout() {
            //Add your own code here
            Log.d("TEST_CODE", "isSystemKeyboardVisible:" + isSystemKeyboardVisible())
        }
    });
}


/**
 * Check system keyboard visibility
 * @return true if visible
 */
public boolean isSystemKeyboardVisible() {
    try {
        final InputMethodManager manager = (InputMethodManager) getContext().getSystemService(Context.INPUT_METHOD_SERVICE);
        final Method windowHeightMethod = InputMethodManager.class.getMethod("getInputMethodWindowVisibleHeight");
        final int height = (int) windowHeightMethod.invoke(manager);
        return height > 0;
    } catch (Exception e) {
        return false;
    }
}

你的代码应该包含 getWindow().getDecorView()。/** * 添加全局布局监听器以观察系统键盘可见性 */ private void initObserverForSystemKeyboardVisibility() { getWindow().getDecorView().getRootView().getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() { @Override public void onGlobalLayout() { //在此处添加自己的代码 Log.d("TEST_CODE", "isSystemKeyboardVisible:" + isSystemKeyboardVisible()); } }); } - Inside 4ndroid
getInputMethodWindowVisibleHeight 方法被阻止,因为使用了非 SDK API 列表。您不能在生产中使用此反射。请在此处查看更多信息:https://developer.android.com/guide/app-compatibility/restrictions-non-sdk-interfaces - RufusInZen

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接