Android中是否有任何方法可以在运行时获取设备上显示的虚拟键盘的高度?其实我想在键盘上方显示一个文本框。
13 回答
为了解决这个问题,我编写了一个可以计算浮动软键盘高度的keyboardHeightProvider。Activity 可以在 AndroidManifest.xml 中设置为adjustNone 或adjustPan。
https://github.com/siebeprojects/samples-keyboardheight
西贝
我为此尝试了许多建议的方法,但似乎没有一个适用于 Android SDL。我认为这要么是因为 SDL 显示是“全屏”,要么是因为它位于“AbsoluteLayout”内,因此“视图”的高度实际上从未改变。这种方法对我有用:
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
}
});
是的,您可以在 Viewtree Observer 和全局布局侦听器的帮助下,尝试以下提到的步骤
- 获取布局的根视图
- 获取该根的 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:layout_alignParentBottom="true"
并在清单文件中进行软输入adjustresize
android:windowSoftInputMode="adjustResize"
那么当键盘出现时文本框会向上移动。
此方法适用于您的活动adjustNothing
或任何windowSoftInputMode
活动。
<activity android:name=".MainActivity"
android:configChanges="orientation|keyboardHidden|screenSize"
android:theme="@style/AppTheme"
android:windowSoftInputMode="stateHidden|adjustNothing"/>
使用 a 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(...)
,因此无论您将活动设置为什么,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..
}
});
//...
}
我创建了一个库项目获取 android 键盘高度,即使活动不使用adjustResize
输入模式。
我的解决方案是上述所有解决方案的组合。这个解决方案也很老套,但解决了问题(至少对我来说)。
- 我在屏幕底部有透明背景的临时视图。
- 我
android:windowSoftInputMode="adjustResize"
在清单中的活动标签中添加了标志,就像@bill 建议的那样。 现在主要故事在
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中以编程方式获取键盘高度并对其进行了测试,请尝试一次:
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;
}
});
谢谢你。
随着导航栏、键盘等被添加到窗口中,您可以测量这些插图以检查键盘是否打开。使用 Android R,您可以直接测量键盘,但您可以回退到从早期版本的插图中计算键盘尺寸。
这适用于棒棒糖及更高版本。
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;
}
});
不要再看了! 很长一段时间以来,我一直在寻找解决方案。在尝试了 SOF 上建议的所有技巧几天后,我找到了真正有效的完美解决方案。
这个 GitHub 项目以最好的方式展示了它: https ://github.com/siebeprojects/samples-keyboardheight
玩得开心!
我终于找到了获得软/虚拟键盘高度的解决方案。我不能说这适用于所有设备,但我在某些设备上尝试过,包括真实设备和模拟器设备,并且它可以工作。我尝试了从 Android API 16 到 29 的设备。这有点棘手。以下是我的分析。
首先,我尝试使用以下功能在屏幕高度和软/虚拟键盘顶部的可见框架高度之间减去getHeightDifference()
。我发现在第一次创建布局时,在 EditText 的焦点上打开键盘之前,差异高度将取决于 Android 系统导航栏,导航栏是否显示在设备屏幕内。因此,如果导航栏在屏幕外,则 的值heightDifference
将为 0,如果在屏幕内,则值将大于 0。我使用systemNavigationBarHeight
Integer 对象的变量(而不是使用原始int
数据)来保存该高度差的第一个值,只需要一个初始化,我假设它是 Nav Bar 的高度。
然后在下一个代码块中,我检查下一个高度差是否大于 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;
}
}
我发现一个非常适合我的解决方案是存储一个全局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
}
}
}