我需要做一件非常简单的事情——找出软键盘是否显示。这在安卓系统中可行吗?
我需要做一件非常简单的事情——找出软键盘是否显示。这在安卓系统中可行吗?
新增答案 于2012年1月25日添加
在撰写以下答案后,有人向我介绍了存在于SDK中自版本1以来一直潜伏着的ViewTreeObserver和相关API。
与其需要自定义布局类型,一个更简单的解决方案是为您的活动根视图(例如@+id/activityRoot
)分配已知ID,将GlobalLayoutListener挂钩到ViewTreeObserver中,并从那里计算您的活动视图根与窗口大小之间的大小差异:
final View activityRootView = findViewById(R.id.activityRoot);
activityRootView.getViewTreeObserver().addOnGlobalLayoutListener(new OnGlobalLayoutListener() {
@Override
public void onGlobalLayout() {
int heightDiff = activityRootView.getRootView().getHeight() - activityRootView.getHeight();
if (heightDiff > dpToPx(this, 200)) { // if more than 200 dp, it's probably a keyboard...
// ... do something here
}
}
});
使用类似以下工具:
public static float dpToPx(Context context, float valueInDp) {
DisplayMetrics metrics = context.getResources().getDisplayMetrics();
return TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, valueInDp, metrics);
}
简单易懂!
注意:
您的应用程序必须在Android清单中设置此标志android:windowSoftInputMode="adjustResize"
,否则上述解决方案将无法正常工作。
原始答案
是的,这是可能的,但比它应该做的要难得多。
如果我需要关心键盘何时出现和消失(这很常见),那么我会自定义我的顶层布局类,覆盖onMeasure()
方法。基本逻辑是,如果布局发现自己填充了比窗口总面积少得多的空间,则可能显示软键盘。
import android.app.Activity;
import android.content.Context;
import android.graphics.Rect;
import android.util.AttributeSet;
import android.widget.LinearLayout;
/*
* LinearLayoutThatDetectsSoftKeyboard - a variant of LinearLayout that can detect when
* the soft keyboard is shown and hidden (something Android can't tell you, weirdly).
*/
public class LinearLayoutThatDetectsSoftKeyboard extends LinearLayout {
public LinearLayoutThatDetectsSoftKeyboard(Context context, AttributeSet attrs) {
super(context, attrs);
}
public interface Listener {
public void onSoftKeyboardShown(boolean isShowing);
}
private Listener listener;
public void setListener(Listener listener) {
this.listener = listener;
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
int height = MeasureSpec.getSize(heightMeasureSpec);
Activity activity = (Activity)getContext();
Rect rect = new Rect();
activity.getWindow().getDecorView().getWindowVisibleDisplayFrame(rect);
int statusBarHeight = rect.top;
int screenHeight = activity.getWindowManager().getDefaultDisplay().getHeight();
int diff = (screenHeight - statusBarHeight) - height;
if (listener != null) {
listener.onSoftKeyboardShown(diff>128); // assume all soft keyboards are at least 128 pixels high
}
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
}
}
然后在您的Activity类中...
public class MyActivity extends Activity implements LinearLayoutThatDetectsSoftKeyboard.Listener {
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
...
LinearLayoutThatDetectsSoftKeyboard mainLayout = (LinearLayoutThatDetectsSoftKeyboard)findViewById(R.id.main);
mainLayout.setListener(this);
...
}
@Override
public void onSoftKeyboardShown(boolean isShowing) {
// do whatever you need to do here
}
...
}
((ViewGroup) findViewById(android.R.id.content)).getChildAt(0)
。 - Goldsmithandroid.R.id.content
),则可以更自信地说System
而不是您的应用程序是更改其高度的实体。让安卓团队给我们一些基本的关于软键盘输入的信息,这样会更加安全。 - GraemeheightDiff
中将始终包括操作栏的高度。在新答案中,通过检查该高度是否大于某个常量来忽略了这一点,但对于像 Nexus 4 这样的 xxhdpi 设备,100 像素不足够。如果您真的想使用这种粗糙的解决方法,请考虑将该值转换为 DP。 - Paul Lammertsma希望这能够帮助到有需要的人。
Reuben Scratton给出的新答案非常棒且高效,但只有在将windowSoftInputMode设置为adjustResize时才有效。如果你将其设置为adjustPan,则仍然无法使用他的代码片段来检测键盘是否可见。为了解决这个问题,我对上面的代码进行了微小的修改。
final View activityRootView = findViewById(R.id.activityRoot);
activityRootView.getViewTreeObserver().addOnGlobalLayoutListener(new OnGlobalLayoutListener() {
@Override
public void onGlobalLayout() {
Rect r = new Rect();
//r will be populated with the coordinates of your view that area still visible.
activityRootView.getWindowVisibleDisplayFrame(r);
int heightDiff = activityRootView.getRootView().getHeight() - r.height();
if (heightDiff > 0.25*activityRootView.getRootView().getHeight()) { // if more than 25% of the screen, its probably a keyboard...
... do something here
}
}
});
ActionBar
和 ActionBarSherlock
。非常感谢!顺便说一下,还有一个方法 r.height()
:) - Dmitry Zaytsevandroid:windowSoftInputMode="adjustNothing"
怎么样?谢谢。我需要将android:windowSoftInputMode
设置为adjustNothing
。 - Jerikc XIONG在计算机领域,这个问题的存在已经很久了,但仍然是一个不可思议的相关问题!
所以我整合并优化了上述答案...
public interface OnKeyboardVisibilityListener {
void onVisibilityChanged(boolean visible);
}
public final void setKeyboardListener(final OnKeyboardVisibilityListener listener) {
final View activityRootView = ((ViewGroup) getActivity().findViewById(android.R.id.content)).getChildAt(0);
activityRootView.getViewTreeObserver().addOnGlobalLayoutListener(new OnGlobalLayoutListener() {
private boolean wasOpened;
private final int DefaultKeyboardDP = 100;
// From @nathanielwolf answer... Lollipop includes button bar in the root. Add height of button bar (48dp) to maxDiff
private final int EstimatedKeyboardDP = DefaultKeyboardDP + (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP ? 48 : 0);
private final Rect r = new Rect();
@Override
public void onGlobalLayout() {
// Convert the dp to pixels.
int estimatedKeyboardHeight = (int) TypedValue
.applyDimension(TypedValue.COMPLEX_UNIT_DIP, EstimatedKeyboardDP, activityRootView.getResources().getDisplayMetrics());
// Conclude whether the keyboard is shown or not.
activityRootView.getWindowVisibleDisplayFrame(r);
int heightDiff = activityRootView.getRootView().getHeight() - (r.bottom - r.top);
boolean isShown = heightDiff >= estimatedKeyboardHeight;
if (isShown == wasOpened) {
Log.d("Keyboard state", "Ignoring global layout change...");
return;
}
wasOpened = isShown;
listener.onVisibilityChanged(isShown);
}
});
}
对我来说没问题 :)
注意: 如果你发现 DefaultKeyboardDP 不适合你的设备,请调整该值并发布评论让大家知道该值应该是什么...最终我们会得到正确的值以适配所有设备!
更多详情请查看在 Cyborg 上的实现。
抱歉回答晚了,但是我创建了一个小帮助类来处理打开/关闭事件并通知监听器和其他有用的事情,也许有人会发现它有帮助:
import android.graphics.Rect;
import android.view.View;
import android.view.ViewTreeObserver;
import java.util.LinkedList;
import java.util.List;
public class SoftKeyboardStateWatcher implements ViewTreeObserver.OnGlobalLayoutListener {
public interface SoftKeyboardStateListener {
void onSoftKeyboardOpened(int keyboardHeightInPx);
void onSoftKeyboardClosed();
}
private final List<SoftKeyboardStateListener> listeners = new LinkedList<SoftKeyboardStateListener>();
private final View activityRootView;
private int lastSoftKeyboardHeightInPx;
private boolean isSoftKeyboardOpened;
public SoftKeyboardStateWatcher(View activityRootView) {
this(activityRootView, false);
}
public SoftKeyboardStateWatcher(View activityRootView, boolean isSoftKeyboardOpened) {
this.activityRootView = activityRootView;
this.isSoftKeyboardOpened = isSoftKeyboardOpened;
activityRootView.getViewTreeObserver().addOnGlobalLayoutListener(this);
}
@Override
public void onGlobalLayout() {
final Rect r = new Rect();
//r will be populated with the coordinates of your view that area still visible.
activityRootView.getWindowVisibleDisplayFrame(r);
final int heightDiff = activityRootView.getRootView().getHeight() - (r.bottom - r.top);
if (!isSoftKeyboardOpened && heightDiff > 100) { // if more than 100 pixels, its probably a keyboard...
isSoftKeyboardOpened = true;
notifyOnSoftKeyboardOpened(heightDiff);
} else if (isSoftKeyboardOpened && heightDiff < 100) {
isSoftKeyboardOpened = false;
notifyOnSoftKeyboardClosed();
}
}
public void setIsSoftKeyboardOpened(boolean isSoftKeyboardOpened) {
this.isSoftKeyboardOpened = isSoftKeyboardOpened;
}
public boolean isSoftKeyboardOpened() {
return isSoftKeyboardOpened;
}
/**
* Default value is zero {@code 0}.
*
* @return last saved keyboard height in px
*/
public int getLastSoftKeyboardHeightInPx() {
return lastSoftKeyboardHeightInPx;
}
public void addSoftKeyboardStateListener(SoftKeyboardStateListener listener) {
listeners.add(listener);
}
public void removeSoftKeyboardStateListener(SoftKeyboardStateListener listener) {
listeners.remove(listener);
}
private void notifyOnSoftKeyboardOpened(int keyboardHeightInPx) {
this.lastSoftKeyboardHeightInPx = keyboardHeightInPx;
for (SoftKeyboardStateListener listener : listeners) {
if (listener != null) {
listener.onSoftKeyboardOpened(keyboardHeightInPx);
}
}
}
private void notifyOnSoftKeyboardClosed() {
for (SoftKeyboardStateListener listener : listeners) {
if (listener != null) {
listener.onSoftKeyboardClosed();
}
}
}
}
使用示例:
final SoftKeyboardStateWatcher softKeyboardStateWatcher
= new SoftKeyboardStateWatcher(findViewById(R.id.activity_main_layout);
// Add listener
softKeyboardStateWatcher.addSoftKeyboardStateListener(...);
// then just handle callbacks
getLastKeyboardHeightInPx()
不包括该行的高度。您知道如何考虑这一点吗? - ygesher避免在高密度设备上错误地检测软键盘的可见性需要一些改进:
高度差的阈值应该定义为128 dp,而不是128像素。
请参考Google设计文档关于度量和网格的内容,48 dp是触摸对象的舒适大小,32 dp是按钮的最小大小。通用软键盘应包括4行键按钮,因此最小键盘高度应为:32 dp * 4 = 128 dp,这意味着阈值大小应通过乘以设备密度转换为像素。对于xxxhdpi设备(密度为4),软键盘高度阈值应为128 * 4 = 512像素。
根视图和其可见区域之间的高度差:
根视图高度 - 状态栏高度 - 可见框架高度 = 根视图底部 - 可见框架底部,因为状态栏高度等于根视图可见框架的顶部。
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;
}
哇,我们有好消息要告诉安卓爱好者们了。是时候告别旧的方法了。 首先,我会添加官方发布说明以了解更多关于这些方法/类的信息,然后我们将看到这些令人惊叹的方法/类。
注意:在这些类/方法发布之前,请勿将其添加到您的发布应用程序中
如何检查键盘可见性
val insets = ViewCompat.getRootWindowInsets(view)
val isKeyboardVisible = insets.isVisible(Type.ime())
很少有其他实用程序
如何获取键盘的高度
val insets = ViewCompat.getRootWindowInsets(view)
val keyboardHeight = insets.getInsets(Type.ime()).bottom
如何显示/隐藏键盘
val controller = view.windowInsetsController
// Show the keyboard
controller.show(Type.ime())
// Hide the keyboard
controller.hide(Type.ime())
注意: WindowInsetsController 是在API-30中添加的,因此请等待兼容性类可用。
如何监听键盘隐藏/显示事件
ViewCompat.setOnApplyWindowInsetsListener(view) { v, insets ->
val isKeyboardVisible = insets.isVisible(Type.ime())
if (isKeyboardVisible) {
// Do it when keyboard is being shown
} else {
// Do it when keyboard is hidden
}
// Return the insets to keep going down this event to the view hierarchy
insets
}
如果您需要同时隐藏键盘并检查软输入状态,则可以使用以下解决方案:
该想法是,如果您需要同时隐藏键盘并检查软输入状态,请使用以下解决方案:public boolean hideSoftInput() {
InputMethodManager imm = (InputMethodManager) getSystemService(Activity.INPUT_METHOD_SERVICE);
return imm.hideSoftInputFromWindow(mViewPager.getWindowToken(), 0);
}
我花了一点时间来理解这个问题...我遇到了一些CastException,但是发现你可以在layout.xml中用类名替换LinearLayout。
像这样:
<?xml version="1.0" encoding="UTF-8"?>
<LinearLayout android:layout_width="fill_parent" android:layout_height="fill_parent"
xmlns:android="http://schemas.android.com/apk/res/android" android:id="@+id/llMaster">
<com.ourshoppingnote.RelativeLayoutThatDetectsSoftKeyboard android:background="@drawable/metal_background"
android:layout_width="fill_parent" android:layout_height="fill_parent"
android:id="@+id/rlMaster" >
<LinearLayout android:layout_width="fill_parent"
android:layout_height="1dip" android:background="@drawable/line"></LinearLayout>
....
</com.ourshoppingnote.RelativeLayoutThatDetectsSoftKeyboard>
</LinearLayout>
这样你就不会遇到任何类型转换问题。
如果你不想在每个页面上都这样做,我建议你使用“Android中的MasterPage”。请查看此链接: http://jnastase.alner.net/archive/2011/01/08/ldquomaster-pagesrdquo-in-android.aspx
https://rogerkeays.com/how-to-check-if-the-software-keyboard-is-shown-in-android
还有一种使用系统插入的解决方案,但是它只适用于API >= 21
(Android L
)。假设你有一个BottomNavigationView
,它是LinearLayout
的子项,并且当键盘显示时需要隐藏它:
> LinearLayout
> ContentView
> BottomNavigationView
LinearLayout
,方法如下:public class KeyboardAwareLinearLayout extends LinearLayout {
public KeyboardAwareLinearLayout(Context context) {
super(context);
}
public KeyboardAwareLinearLayout(Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
}
public KeyboardAwareLinearLayout(Context context,
@Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
public KeyboardAwareLinearLayout(Context context, AttributeSet attrs,
int defStyleAttr, int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
}
@Override
public WindowInsets onApplyWindowInsets(WindowInsets insets) {
int childCount = getChildCount();
for (int index = 0; index < childCount; index++) {
View view = getChildAt(index);
if (view instanceof BottomNavigationView) {
int bottom = insets.getSystemWindowInsetBottom();
if (bottom >= ViewUtils.dpToPx(200)) {
// keyboard is shown
view.setVisibility(GONE);
} else {
// keyboard is hidden
view.setVisibility(VISIBLE);
}
}
}
return insets;
}
}
这个想法是当键盘弹出时,系统插入值会变化,.bottom
值会有很大的改变。