【问题标题】:Is there any way in Android to get the height of virtual keyboard of deviceAndroid中有什么方法可以获取设备虚拟键盘的高度
【发布时间】:2013-05-28 09:50:00
【问题描述】:

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

【问题讨论】:

  • 类似问题here :)
  • 但它是在 2 年前被问到的。现在可能有一些解决方案。
  • @ZeeshanMirza 这实际上是今天... :)

标签: android sdk


【解决方案1】:

为了解决这个问题,我编写了一个可以计算浮动软键盘高度的keyboardHeightProvider。 Activity可以在AndroidManifest.xml中设置为adjustNone或者adjustPan。

https://github.com/siebeprojects/samples-keyboardheight

西贝

【讨论】:

  • 10+ 完美的兄弟!
  • 这个项目中是否有任何 gradle 依赖或 jar 可用?
  • 很高兴能帮上忙。很高兴听到它运作良好。
  • 此代码在带有缺口的手机上不起作用,我的高度错误...
  • 干得好!它可以在 windowSoftInputMode 为 adjustNothing 时使用。
【解决方案2】:

我为此尝试了许多建议的方法,但似乎没有一个适用于 Android SDL。我认为这要么是因为 SDL 显示是“全屏”,要么是因为它位于“AbsoluteLayout”内,因此“视图”的高度实际上从未改变。这种方法对我有用:

Getting the dimensions of the soft keyboard

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

【讨论】:

  • 感谢您的回答,正是我所需要的。这适用于在没有 adjustResize (我不想要的)的情况下计算屏幕键盘大小。
  • 这是一个很好的答案。 @Dave Lawrence 你应该在答案中提到矩形的高度没有考虑状态栏。
  • 如何用这种方法计算键盘高度?
  • 天哪!我发现了什么隐藏的宝石:)
  • 在我的例子中:int heightDiff = mRootView.getHeight() - r.bottom;工作正常
【解决方案3】:

是的,您可以在 Viewtree Observer 和全局布局监听器的帮助下,尝试以下提到的步骤

  1. 获取布局的根视图
  2. 获取此根的 Viewtree 观察者,并在其之上添加一个全局布局侦听器。

现在,每当显示软键盘时,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.  
        }
  });

问候, 技术拳

【讨论】:

  • 我认为这只适用于 android:windowSoftInputMode="adjustResize"
  • 这只是获取键盘是否显示,但它不会给你键盘的高度。
  • Heightdiff会给你,键盘占用的空间量。
  • 这个高度是像素还是dp?
  • 150px 对于像 Nexus 5 这样的 xxhdpi 设备来说是不够的。最好将这个绝对像素值从 dp 值转换并使用它。我使用120dp,它适用于 Nexus 7 (xhdpi) 和 Nexus 5 (xxhdpi)。不过,您的经历可能会有所不同...
【解决方案4】:

将文本框作为父底部。

android:layout_alignParentBottom="true"

并在清单文件中进行软输入adjustresize

android:windowSoftInputMode="adjustResize"

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

【讨论】:

  • 我有一个全屏视频视图,当布局调整大小时我不想移动它,但我仍然希望文本框移动并保持在键盘上方。如何做到这一点?
  • @Lo-Tan 这正是我想要实现的。你知道怎么做吗?
【解决方案5】:

你说不出来。不,真的:你根本看不出来。

键盘不需要是任何特定的形状。它不必放在屏幕底部(manymost popular 选项are not),当您更改文本字段时,它不必保持其当前大小(几乎没有,取决于标志)。它甚至不必是rectangular。它也可能只是接管entire screen

(我对类似问题的回答的副本,Getting the dimensions of the soft keyboard

【讨论】:

    【解决方案6】:

    此方法适用于您的活动中的adjustNothing 或任何windowSoftInputMode

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

    使用PopupWindow,您可以为其设置单独的“键盘行为”,它会通知您键盘的大小。 PopupWindow 具有屏幕的高度,但宽度为 0px,因此您不会看到它,它不会影响您的活动,但会为您提供所需的信息。

    创建一个名为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(...),所以不管你把activity 设置成什么,PopupWindow 仍然会受到键盘打开或关闭的影响,并且会提供高度的父activity。如果高度为&gt;= 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..
            }
        });
    
        //...
    }
    

    【讨论】:

    • 你没有得到足够的功劳。数十篇文章和 SO 问题,人们未能成功地尝试使其与 AdjustNothing 一起工作。就是这个。谢谢。
    • @Will 很高兴我能帮上忙 :)
    【解决方案7】:

    我创建了一个获取 android 键盘高度的库项目,即使活动不使用 adjustResize 输入模式。

    https://github.com/Crysis21/KeyboardHeightProvider

    【讨论】:

      【解决方案8】:

      我的解决方案是上述所有解决方案的组合。这个解决方案也很老套,但解决了问题(至少对我来说)。

      1. 我在屏幕底部有透明背景的临时视图上的位置。
      2. 我在清单中的活动标记中添加了android:windowSoftInputMode="adjustResize" 标志,就像@bill 建议的那样。
      3. 现在主要故事在onGlobalLayout()。在那里我计算了临时视图的 y 轴和根视图的高度之间的差异

        final View view = findViewById(R.id.base);
        
        view.getViewTreeObserver().addOnGlobalLayoutListener(new OnGlobalLayoutListener() {
        
            @Override
            public void onGlobalLayout() {
        
                int rootViewHeight = view.getRootView().getHeight();
                View tv = findViewById(R.id.temp_view);
                int location[] = new int[2];
                tv.getLocationOnScreen(location);
                int height = (int) (location[1] + tv.getMeasuredHeight());
                deff = rootViewHeight - height;
                // deff is the height of soft keyboard
        
            }
        });
        

      但无论如何要解决@zeeshan0026 的问题,只需在清单android:windowSoftInputMode="adjustResize" 中添加一个标志就足够了。

      【讨论】:

      • 是否可以使用android:windowSoftInputMode="adjustNothing"获取键盘高度?
      • 如果我们使用 adjustNothing 标志,那么当键盘打开时视图将不会再次呈现,因此显然 onGlobalLayout 不应该调用并且我们将无法获得大小的变化。虽然我没有自己尝试,但这是预期的。
      【解决方案9】:

      我已经用它在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;
                      }
                  });
      

      谢谢。

      【讨论】:

      • 不要忘记删除回调中的OnGlobalLayoutListener
      • 我使用相同的方法,但仍然有些设备似乎有问题。一款带有自定义键盘的 Galaxy s9 似乎在这方面失败了。
      • @htafoya 你有没有为 S9 (plus) 找到任何解决方案?
      【解决方案10】:

      随着导航栏、键盘等被添加到窗口中,您可以测量这些插图以检查键盘是否打开。使用 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;
                         }
                     });
      

      【讨论】:

        【解决方案11】:

        别再看了! 很长一段时间以来,我一直在寻找解决方案。在尝试了 SOF 上建议的所有技巧几天后,我找到了真正有效的完美解决方案。

        这个 GitHub 项目以最好的方式展示了它: https://github.com/siebeprojects/samples-keyboardheight

        玩得开心!

        【讨论】:

          【解决方案12】:

          我终于找到了获得软/虚拟键盘高度的解决方案。我不能说这适用于所有设备,但我在某些设备上尝试过,包括真实设备和模拟器设备,并且它可以工作。我尝试了从 Android API 16 到 29 的设备。这有点棘手。这是我的分析。

          首先,我尝试通过使用getHeightDifference() 的函数来减去屏幕高度和软/虚拟键盘顶部可见框架的高度,如下所示。我发现在第一次创建布局时,在键盘打开 EditText 的焦点之前,差异高度将取决于 Android 系统导航栏,导航栏是否显示在设备屏幕内。因此,如果导航栏在屏幕外,heightDifference 的值将为 0,如果在屏幕内,则大于 0。我使用带有整数对象的systemNavigationBarHeight 变量(而不是使用原始int 数据)来保存该高度差的第一个值,只需要一个初始化,我假设它是导航栏的高度。

          然后在下一个代码块中,我检查下一个高度差是否大于 Android 系统中实际导航栏的高度(或者默认为 100,以防 Android 系统中没有导航栏),然后我减去再次使用systemNavigationBarHeight 的值来获取软/虚拟键盘的真实高度。

          希望这对查找另一个答案的人有所帮助。

          public class MyActivity extends AppCompatActivity {
          
              @Override
              protected void onCreate(Bundle savedInstanceState) {
                  super.onCreate(savedInstanceState);
                  
                  rootView.getViewTreeObserver()
                          .addOnGlobalLayoutListener(new OnGlobalLayoutListener() {
                              private Integer systemNavigationBarHeight = null;
                                  
                              @Override
                              public void onGlobalLayout() {
                                  int heightDifference = getHeightDifference();
                                  if (heightDifference > 0) {
                                      if (systemNavigationBarHeight == null) {
                                          /* Get layout height when the layout was created at first time */
                                          systemNavigationBarHeight = heightDifference;
                                      }
                                  } else {
                                      systemNavigationBarHeight = 0;
                                  }
          
                                  if (heightDifference > getDefaultNavigationBarHeight()) {
                                      /* Keyboard opened */
                                      int keyBoardHeight = heightDifference - systemNavigationBarHeight;
                                  } else {
                                      /* Keyboard closed */
                                  }
                              }
                  }
              }
              
              private int getHeightDifference() {
                  Point screenSize = new Point();
                  if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
                      getWindowManager().getDefaultDisplay().getRealSize(screenSize);
                  } else {
                      getWindowManager().getDefaultDisplay().getSize(screenSize);
                  }
          
                  Rect rect = new Rect();
                  rootView.getWindowVisibleDisplayFrame(rect);
                  return screenSize.y - rect.bottom;
              }
              
              private int getDefaultNavigationBarHeight() {
                  int resourceId = getResources().getIdentifier("navigation_bar_height", "dimen", "android");
                  if (resourceId > 0) {
                      return getResources().getDimensionPixelSize(resourceId);
                  }
                  return 100;
              }
          }
          

          【讨论】:

          【解决方案13】:

          我发现一个非常适合我的解决方案是存储一个全局 bottom 值,然后添加一个 TreeView Observer 并将新的底部值与存储的值进行比较。使用android:windowSoftInputMode="adjustResize"

          private var bottom: Int = 0
          
          override fun onCreate(savedInstanceState: Bundle?) {
              super.onCreate(savedInstanceState)
              
              val rect = Rect()
              this.window.decorView.getWindowVisibleDisplayFrame(rect)
              this.bottom = rect.bottom
          
              this.window.decorView.viewTreeObserver.addOnGlobalLayoutListener {
                  
                  val newRect = Rect()
                  this.window.decorView.getWindowVisibleDisplayFrame(newRect)
                  
                  // the answer
                  val keyboardHeight = this.bottom - newRect.bottom
          
                  // also
                  if (newRect.bottom < this.bottom) {
                      //keyboard is open
                      
                  } else {
                      //keyboard is hide
                      
                  }
              }
          }
          

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 2015-05-06
            • 2013-03-05
            • 1970-01-01
            • 2016-12-31
            • 2011-04-20
            • 1970-01-01
            • 1970-01-01
            • 2013-09-19
            相关资源
            最近更新 更多