在Android中有没有一种方法可以获取设备虚拟键盘的高度?

64

在Android中,是否有一种方法可以在运行时获取设备上显示的虚拟键盘的高度?实际上,我想在键盘上方显示一个文本框。


在Android中获取虚拟键盘的高度。 - Oli
2
但是这个问题是2年前提出的,现在可能已经有一些解决方案了。 - Zeeshan Mirza
16个回答

57

这个项目中是否有任何Gradle依赖项或JAR可用? - Vijay
4
这段代码在有刘海的手机上不起作用,我得到的高度是错误的…… - Aman Verma
我以为这个很长时间以来都运行良好,但是后来我发现在平板电脑模式下Chromebook的高度有问题:'( - 0101100101
3
太棒了!它可以在 windowSoftInputMode 被设置为 adjustNothing 时使用。 - Fantasy Fang
嗨,我只是想让这里的其他人知道,这种解决方案并不总是有效的。有很多原因可能导致它无法正常工作,比如操作系统特定的API、分屏模式、窗口模式、沉浸模式或凹口等。 - AndroidEngineX
显示剩余4条评论

49

我尝试了很多建议的方法,但似乎都不适用于Android SDL。我认为这可能是因为SDL显示是“全屏”的,或者它位于“AbsoluteLayout”中,因此“View”的高度实际上从未更改。这种方法对我有效:

获取软键盘的尺寸

Window mRootWindow = getWindow();
View mRootView = mRootWindow.getDecorView().findViewById(android.R.id.content);
mRootView.getViewTreeObserver().addOnGlobalLayoutListener(
    new ViewTreeObserver.OnGlobalLayoutListener() {
    public void onGlobalLayout(){
        Rect r = new Rect();
        View view = mRootWindow.getDecorView();
        view.getWindowVisibleDisplayFrame(r);
        // r.left, r.top, r.right, r.bottom
    }
    });

4
谢谢你的回答,正是我所需要的。这对于计算屏幕键盘大小很有用,而不需要使用adjustResize功能(这不是我想要的)。 - Alexey Yakovenko
1
这是一个非常好的答案。@Dave Lawrence,你应该在回答中提到Rect的高度不包括状态栏。 - Ari
1
如何使用这种方法计算键盘的高度? - Ali
3
在我的情况下:int heightDiff = mRootView.getHeight() - r.bottom; 运行良好。 - Usman Rana

37

使用Viewtree Observer和全局布局监听器,您可以实现这一点,只需按照以下步骤尝试即可:

  1. 获取布局的根视图
  2. 为此根视图获取Viewtree observer,并在其上添加全局布局监听器。

现在,每当Android显示软键盘时,它会重新调整屏幕大小并在监听器上调用您。这就是全部要做的事情,现在您需要做的唯一一件事情是计算在重新调整大小后您的根视图高度与原始大小之间的差异。如果差异大于150,则将其视为键盘已充气。

以下是示例代码:

root.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener(){
     public void onGlobalLayout(){
           int heightDiff = root.getRootView().getHeight()- root.getHeight();
           // IF height diff is more then 150, consider keyboard as visible.  
        }
  });

敬礼, Techfist


34
我认为这只有在 android:windowSoftInputMode="adjustResize" 的情况下才有效。 - LiangWang
3
这只是检查键盘是否显示,但它不会提供键盘的高度。 - what is sleep
Heightdiff将为您提供键盘占用的空间量。 - Techfist
这个高度是以像素还是dp为单位? - Ali Shah Ahmed
1
150像素对于Nexus 5这样的xxhdpi设备来说是不够的。最好将这个绝对像素值从dp值转换并使用它。我使用120dp,这在Nexus 7(xhdpi)和Nexus 5(xxhdpi)上效果很好。但你的经验可能会有所不同... - Thomas Keller
显示剩余3条评论

17

2022解决方案

通过新的窗口插图API,这非常简单:

WindowInsetsCompat insets = ViewCompat.getRootWindowInsets(activity.getWindow().getDecorView());
//Enjoy your keyboard height
int keyboardHeight = insets.getInsets(WindowInsetsCompat.Type.ime()).bottom;
你也可以轻松监听键盘的显示/隐藏事件:

你也可以轻松监听键盘的显示/隐藏事件:

ViewCompat.setOnApplyWindowInsetsListener(activity.getWindow().getDecorView(), (v, insets) -> {
   boolean isKeyboardVisible = insets.isVisible(WindowInsetsCompat.Type.ime());
   int keyboardHeight = insets.getInsets(WindowInsetsCompat.Type.ime()).bottom;
   
   //Do your job here

   return insets;
});

在这里阅读更多


1
这很完美,只是它会让状态栏的颜色消失并变成白色,有解决这个问题的方法吗? - Peter
根据Peter的评论,检索到的键盘高度也不正确。从我的肉眼观察来看,返回的值似乎是(实际键盘高度+状态栏高度)。 - Cheok Yan Cheng
@Peter 我认为你只需要在使用新的WindowInsets API时更新一些XML设置。请参考此链接 - zuko
@CheokYanCheng 这个设置会影响计算:WindowCompat.setDecorFitsSystemWindows(window, false)。无论如何,您可以通过获取顶部和底部插图的组合高度来验证键盘高度:Insets.getInsets(WindowInsets.Type.navigationBars()).bottom。还有其他 WindowInsets.Type 的选项。 - zuko
干得不错!但是现在布局占据了整个高度(底部元素被导航栏遮挡住了)。在“return insets;”之前,你应该自定义插入。 - CoolMind
显示剩余2条评论

9

这种方法适用于你的活动中的adjustNothing或任何windowSoftInputMode

<activity android:name=".MainActivity"
        android:configChanges="orientation|keyboardHidden|screenSize"
        android:theme="@style/AppTheme"
        android:windowSoftInputMode="stateHidden|adjustNothing"/>

使用PopupWindow,您可以为其单独设置“键盘行为”,并且它将通知您键盘的大小。PopupWindow 的高度为屏幕高度,但宽度为 0 像素,因此您看不到它,它也不会影响您的活动,但会提供您所需的信息。
创建名为KeyboardHeightProvider的类并添加以下代码:
public class KeyboardHeightProvider extends PopupWindow {
    public KeyboardHeightProvider(Context context, WindowManager windowManager, View parentView, KeyboardHeightListener listener) {
        super(context);

        LinearLayout popupView = new LinearLayout(context);
        popupView.setLayoutParams(new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT));
        popupView.getViewTreeObserver().addOnGlobalLayoutListener(() -> {
            DisplayMetrics metrics = new DisplayMetrics();
            windowManager.getDefaultDisplay().getMetrics(metrics);

            Rect rect = new Rect();
            popupView.getWindowVisibleDisplayFrame(rect);

            int keyboardHeight = metrics.heightPixels - (rect.bottom - rect.top);
            int resourceID = context.getResources().getIdentifier("status_bar_height", "dimen", "android");
            if (resourceID > 0) {
                keyboardHeight -= context.getResources().getDimensionPixelSize(resourceID);
            }
            if (keyboardHeight < 100) {
                keyboardHeight = 0;
            }
            boolean isLandscape = metrics.widthPixels > metrics.heightPixels;
            boolean keyboardOpen = keyboardHeight > 0;
            if (listener != null) {
                listener.onKeyboardHeightChanged(keyboardHeight, keyboardOpen, isLandscape);
            }
        });

        setContentView(popupView);

        setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE | WindowManager.LayoutParams.SOFT_INPUT_STATE_ALWAYS_VISIBLE);
        setInputMethodMode(PopupWindow.INPUT_METHOD_NEEDED);
        setWidth(0);
        setHeight(ViewGroup.LayoutParams.MATCH_PARENT);
        setBackgroundDrawable(new ColorDrawable(0));

        parentView.post(() -> showAtLocation(parentView, Gravity.NO_GRAVITY, 0, 0));
    }

    public interface KeyboardHeightListener {
        void onKeyboardHeightChanged(int keyboardHeight, boolean keyboardOpen, boolean isLandscape);
    }
}

请注意PopupWindow有自己的setSoftInputMode(...),因此无论您设置活动内容如何,PopupWindow仍会受到键盘打开或关闭的影响,并提供父活动的高度。如果高度>= 100,则可以假定键盘已打开。

要使用它,请在您的Activity的onCreate(...)方法中的setContentView(...)之后实例化它:

@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.main_activity);

    LinearLayout llRoot = findViewById(R.id.llRoot); //The root layout (Linear, Relative, Contraint, etc...)

    new KeyboardHeightProvider(this, getWindowManager(), llRoot, new KeyboardHeightProvider.KeyboardHeightListener() {
        @Override
        public void onKeyboardHeightChanged(int keyboardHeight, boolean keyboardOpen, boolean isLandscape) {
            Log.i("keyboard listener", "keyboardHeight: " + keyboardHeight + " keyboardOpen: " + keyboardOpen + " isLandscape: " + isLandscape);

            //Do what you want or have to with the parameters..
        }
    });

    //...
}

1
你没有得到足够的赞誉。有许多文章和 Stack Overflow 问题,人们试图使用“AdjustNothing”使其工作失败。这就是它了。谢谢。 - Will
我在键盘上方有额外的空间,减去“resourceID”后问题得到解决。 - adwardwo1f
1
这个代码可以运行,但是在旋转设备时会出现内存泄漏警告。我通过在ActivityonPause方法中调用PopupWindowdismiss方法来解决了这个问题。 - tagy22

8
将文本框放在父容器底部。
android:layout_alignParentBottom="true"

在清单文件中,将软输入法设置为adjustresize

android:windowSoftInputMode="adjustResize"

当键盘出现时,文本框会向上移动。

2
我有一个全屏的视频视图,当布局调整大小时,我不想移动它,但我仍然希望文本框移动并保持在键盘上方。如何实现这一点? - Lo-Tan
@Lo-Tan 这正是我想要实现的。你找到方法了吗? - pesho

7

你无法判断。不,真的:你无法判断。

键盘不需要任何特定形状。它不必放在屏幕底部(许多 最受欢迎的 选项并非如此),当您更改文本字段时,它不必保持当前大小(几乎没有根据标志进行调整)。它甚至不必是矩形的。它也可以占据整个屏幕

(这是我在类似问题上的回答,获取软键盘的尺寸 的副本)


4

2
随着导航栏、键盘等添加到窗口中,您可以测量这些插入物来检查键盘是否打开。在Android R上,您可以直接测量键盘大小,但是在之前的版本中,您可以从插入物中计算键盘大小。此功能适用于Lollipop及以上版本。
getWindow().getDecorView()
               .setOnApplyWindowInsetsListener(new View.OnApplyWindowInsetsListener() {
             
                   @Override
                   public WindowInsets onApplyWindowInsets(View v, WindowInsets insets) {
                       if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
                           mKeyboardShowing =
                                   insets.getInsets(WindowInsets.Type.ime()).bottom > 0;
                           if (mKeyboardShowing) {
                               setKeyboardHeight(
                                       insets.getInsets(WindowInsets.Type.ime()).bottom -
                                       insets.getInsets(WindowInsets.Type.navigationBars()).bottom);
                           }
                       } else {
                           mKeyboardShowing = getNavigationBarHeight() !=
                                              insets.getSystemWindowInsetBottom();
                           if (mKeyboardShowing) {
                               setKeyboardHeight(insets.getSystemWindowInsetBottom() -
                                                 getNavigationBarHeight());
                           }
                       }
                       return v.onApplyWindowInsets(insets);
                   }

                   public int getNavigationBarHeight() {
                       boolean hasMenuKey = ViewConfiguration.get(MainActivity.this)
                                                             .hasPermanentMenuKey();
                       int resourceId = getResources().getIdentifier("navigation_bar_height",
                                                                     "dimen",
                                                                     "android");
                       if (resourceId > 0 && !hasMenuKey) {
                           return getResources().getDimensionPixelSize(resourceId);
                       }
                       return 0;
                   }
               });

navigation_bar_height 没有公开的方法,并且不能保证在所有类型的设备上都能正常工作。 - AndroidEngineX

1

我已经在Android中使用了这个方法来动态获取键盘高度并进行测试,请尝试一下:

myLayout.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {

                @Override
                public void onGlobalLayout() {
                    // TODO Auto-generated method stub
                    Rect r = new Rect();
                    parent.getWindowVisibleDisplayFrame(r);

                    int screenHeight = parent.getRootView().getHeight();
                    int heightDifference = screenHeight - (r.bottom - r.top);
                    Log.d("Keyboard Size", "Size: " + heightDifference);

                    //boolean visible = heightDiff > screenHeight / 3;
                }
            });

谢谢。

1
不要忘记在回调函数中移除 OnGlobalLayoutListener - Adib Faramarzi
1
我使用相同的方法,但仍然有一些设备似乎存在问题。一个带有自定义键盘的Galaxy S9似乎在这方面出现了故障。 - htafoya
@htafoya,你找到S9(plus)的解决方案了吗? - Ponomarenko Oleh

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