如何检查软件键盘在Android的可见性?

我需要做一件非常简单的事情-找出软件键盘是否显示。这在Android中可行吗?

301736 次浏览

InputMethodManager有关于软键盘的信息。你从一个活动中获得它:

((InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE))

你可以摆弄一下,看看它能告诉你什么。你可以用它来显示或隐藏软输入…

# EYZ0 # EYZ1

在写下下面的答案后,有人告诉我ViewTreeObserver和其他api的存在,这些api从版本1开始就潜伏在SDK中。

而不是需要一个自定义的布局类型,一个更简单的解决方案是给你的活动的根视图一个已知的ID,比如@+id/activityRoot,钩子一个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 Manifest 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);
}


}

然后在活动课上…

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
}


...
}

我花了一点时间才想明白……我运行它一些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>

这样你就不会遇到任何演员问题。

< p >…如果你不想在每个页面上都这样做,我建议你使用“安卓版MasterPage”。点击这里查看链接: # EYZ0 < / p >

我发现@Reuben_Scratton的方法与@Yogesh的方法的组合似乎效果最好。将他们的方法结合起来会产生如下结果:

final View activityRootView = findViewById(R.id.activityRoot);
activityRootView.getViewTreeObserver().addOnGlobalLayoutListener(new OnGlobalLayoutListener() {
@Override
public void onGlobalLayout() {
if (getResources().getConfiguration().keyboardHidden == Configuration.KEYBOARDHIDDEN_NO) { // Check if keyboard is not hidden
// ... do something here
}
}
});

希望这能帮助到大家。

Reuben Scratton给出的新答案非常棒,而且非常有效,但它只在你将windowSoftInputMode设置为adjuststresize时才有效。如果你将它设置为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
}
}
});

检查元素的高度是不可靠的,因为一些键盘,如WifiKeyboard的高度为零。

相反,您可以使用showSoftInput()和hideSoftInput()的回调结果来检查键盘的状态。完整的细节和示例代码在

https://rogerkeays.com/how-to-check-if-the-software-keyboard-is-shown-in-android

而不是假设差异编码,我做了这样的事情,因为我在我的应用程序中有菜单选项。

final View root= findViewById(R.id.myrootview);
root.getViewTreeObserver().addOnGlobalLayoutListener(new OnGlobalLayoutListener() {
@Override
public void onGlobalLayout() {
int heightDiff = root.getRootView().getHeight() - root.getHeight();


Rect rectgle= new Rect();
Window window= getWindow();
window.getDecorView().getWindowVisibleDisplayFrame(rectgle);
int contentViewTop=
window.findViewById(Window.ID_ANDROID_CONTENT).getTop();
if(heightDiff <= contentViewTop){
//Soft KeyBoard Hidden
}else{
//Soft KeyBoard Shown
}
}
});

就计算机而言,这是永远的问题,但这个问题仍然令人难以置信地相关!

所以我把上面的答案结合起来,做了一些改进……

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不适合你的设备玩的值,并发表评论让大家知道应该是什么值…最终我们将得到适合所有设备的正确值!< / p >

有关更多详细信息,请查看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

这个想法是,如果你需要隐藏你的键盘,同时检查软输入状态,使用以下解决方案:

public boolean hideSoftInput() {
InputMethodManager imm = (InputMethodManager) getSystemService(Activity.INPUT_METHOD_SERVICE);
return imm.hideSoftInputFromWindow(mViewPager.getWindowToken(), 0);
}

如果在隐藏之前显示键盘,则此方法返回true。

你可以通过使用活动的decorView来观察软键盘的隐藏。

public final class SoftKeyboardUtil {
public static final String TAG = "SoftKeyboardUtil";
public static void observeSoftKeyBoard(Activity activity , final OnSoftKeyBoardHideListener listener){
final View decorView = activity.getWindow().getDecorView();
decorView.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
@Override
public void onGlobalLayout() {
Rect rect = new Rect();
decorView.getWindowVisibleDisplayFrame(rect);
int displayHight = rect.bottom - rect.top;
int hight = decorView.getHeight();
boolean hide = (double)displayHight / hight > 0.8 ;
if(Log.isLoggable(TAG, Log.DEBUG)){
Log.d(TAG ,"DecorView display hight = "+displayHight);
Log.d(TAG ,"DecorView hight = "+ hight);
Log.d(TAG, "softkeyboard visible = " + !hide);
}


listener.onSoftKeyBoardVisible(!hide);


}
});
}






public interface OnSoftKeyBoardHideListener{
void onSoftKeyBoardVisible(boolean visible);
}
}

我使用了与鲁班的答案略有不同的答案,事实证明在某些情况下更有帮助,特别是在使用高分辨率设备时。

final View activityRootView = findViewById(android.R.id.content);
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
}
}
});

这是我的解决方案,它很有效。而不是寻找像素大小,只是检查内容视图的高度是否改变:

// Scroll to the latest comment whenever the keyboard is shown
commentsContent.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {


private int oldHeight;


@Override
public void onGlobalLayout() {
int newHeight = commentsContent.getMeasuredHeight();
if (newHeight < oldHeight) {
// Check for the keyboard showing in case the height difference
// is a result of orientation change
if (isSoftKeyboardShowing(CommentsActivity.this)) {
// Keyboard is showing so scroll to the latest comment
scrollToLatestComment();
}
}
oldHeight = newHeight;
}


});




public static boolean isSoftKeyboardShowing(Activity activity) {
InputMethodManager inputMethodManager = (InputMethodManager) activity.getSystemService(Activity.INPUT_METHOD_SERVICE);
return inputMethodManager.isActive();
}

一些改进,以避免在高密度设备上错误地检测软键盘的可见性:

  1. 高度差阈值应该定义为128 dp,而不是128像素 参考谷歌关于度量和网格的设计文档48 dp是触摸对象的舒适大小,32 dp是按钮的最小大小。一般的软键盘应该包括4行按键,所以最小键盘高度应该是:32dp * 4 = 128dp,这意味着阈值大小应该通过乘以设备密度转移到像素。对于xxxhdpi设备(密度为4),软键盘高度阈值为128 * 4 = 512像素

  2. 根视图与其可见区域的高度差值:
    根视图高度-状态栏高度-可见帧高度=根视图底部-可见帧底部,因为状态栏高度等于根视图可见帧的顶部。< / p >

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

不要写硬代码。最好的方法是,你必须调整你的视图,而Get Focus on EditText与键盘显示。 你可以使用下面的代码在Manifest文件中添加活动的resize属性

# EYZ0

我的答案基本上与Kachi的答案相同,但我将它包装到一个很好的helper类中,以清理它在整个应用程序中的使用方式。

import android.app.Activity;
import android.app.Fragment;
import android.graphics.Rect;
import android.view.View;
import android.view.ViewTreeObserver.OnGlobalLayoutListener;


/**
* Detects Keyboard Status changes and fires events only once for each change
*/
public class KeyboardStatusDetector {
KeyboardVisibilityListener visibilityListener;


boolean keyboardVisible = false;


public void registerFragment(Fragment f) {
registerView(f.getView());
}


public void registerActivity(Activity a) {
registerView(a.getWindow().getDecorView().findViewById(android.R.id.content));
}


public KeyboardStatusDetector registerView(final View v) {
v.getViewTreeObserver().addOnGlobalLayoutListener(new OnGlobalLayoutListener() {
@Override
public void onGlobalLayout() {
Rect r = new Rect();
v.getWindowVisibleDisplayFrame(r);


int heightDiff = v.getRootView().getHeight() - (r.bottom - r.top);
if (heightDiff > 100) { // if more than 100 pixels, its probably a keyboard...
/** Check this variable to debounce layout events */
if(!keyboardVisible) {
keyboardVisible = true;
if(visibilityListener != null) visibilityListener.onVisibilityChanged(true);
}
} else {
if(keyboardVisible) {
keyboardVisible = false;
if(visibilityListener != null) visibilityListener.onVisibilityChanged(false);
}
}
}
});


return this;
}


public KeyboardStatusDetector setVisibilityListener(KeyboardVisibilityListener listener) {
visibilityListener = listener;
return this;
}


public static interface KeyboardVisibilityListener {
public void onVisibilityChanged(boolean keyboardVisible);
}
}

你可以使用它来检测整个应用程序中的键盘变化,如下所示:

    new KeyboardStatusDetector()
.registerFragment(fragment)  //register to a fragment
.registerActivity(activity)  //or register to an activity
.registerView(view)          //or register to a view
.setVisibilityListener(new KeyboardVisibilityListener() {
@Override
public void onVisibilityChanged(boolean keyboardVisible) {
if(keyboardVisible) {
//Do stuff for keyboard visible
}else {
//Do stuff for keyboard hidden
}
}
});

注意:只使用一个“寄存器”调用。它们都是一样的,只是为了方便

这些解决方案都不适用于棒棒糖。在棒棒糖中,activityRootView.getRootView().getHeight()包含按钮栏的高度,而测量视图则不包含。我已经采用了上面最好/最简单的解决方案来处理棒棒糖。

    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.bottom - r.top);
Resources res = getResources();
// The status bar is 25dp, use 50dp for assurance
float maxDiff =
TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 50, res.getDisplayMetrics());


//Lollipop includes button bar in the root. Add height of button bar (48dp) to maxDiff
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
float buttonBarHeight =
TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 48, res.getDisplayMetrics());
maxDiff += buttonBarHeight;
}
if (heightDiff > maxDiff) { // if more than 100 pixels, its probably a keyboard...
...do something here
}
}
});

有一个直接的方法来找出这个。并且,它不需要任何布局更改 所以,它也可以在沉浸式全屏模式下工作 窍门是您尝试隐藏或显示软键盘,并捕获该尝试的结果 没有恐慌,这并不真正显示或隐藏键盘。我们只要求状态

为了保持最新,你可以简单地重复这个操作,例如每200毫秒,使用Handler.

这里有一个实现:https://stackoverflow.com/a/27567074/2525452

我认为这个方法将帮助你找出是键盘是可见的或不。

 public Boolean isSoftKeyBoardVisible(){
InputMethodManager imm = (InputMethodManager)getSystemService(Context.INPUT_METHOD_SERVICE);


if (imm.isAcceptingText()) {
Log.d(TAG,"Software Keyboard was shown");
return true;
} else {
Log.d(TAG,"Software Keyboard was not shown");
return false;
}


}

如果你设置半透明状态栏模式,Reuben Scratton的新答案(计算高度差int heightDiff = activityRootView.getRootView().getHeight() - activityRootView.getHeight();)将无法在活动中工作。

如果你使用半透明状态栏,activityRootView.getHeight()将永远不会改变软键盘是否可见。它将始终返回活动和状态栏的高度。

例如,Nexus 4, Android 5.0.1,将android:windowTranslucentStatus设置为true,它将永远返回1184,即使ime已经打开。如果你设置android:windowTranslucentStatus为false,它将正确返回Height,如果ime不可见,它将返回1134(不包括状态栏)。关闭ime,它可能会返回5xx(取决于ime的高度)

我不知道这是否是一个bug,我已经尝试了4.4.4和5.0.1,结果是一样的。

所以,到目前为止,第二个最被认可的答案,Kachi的解决方案将是最安全的方法来计算ime的高度。以下是一份复印件:

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.bottom - r.top);
if (heightDiff > 100) { // if more than 100 pixels, its probably a keyboard...
... do something here
}
}
});

你可以试试这个,对我来说很管用:

InputMethodManager imm = (InputMethodManager) getActivity().getSystemService(Context.INPUT_METHOD_SERVICE);


if (imm.isAcceptingText()) {
//Software Keyboard was shown..
} else {
//Software Keyboard was not shown..
}

不需要LayoutListener . #的方法

在我的情况下,我想在替换我的片段之前保存键盘的状态。我从onSaveInstanceState调用方法hideSoftInputFromWindow,它将关闭键盘并返回键盘是否可见。

这个方法很简单,但可能会改变键盘的状态。

在使用上述大多数建议添加固定数字的解决方案时,我刚刚遇到了一个错误。

S4的dpi很高,导致导航栏的高度为100px,因此我的应用程序认为键盘一直是打开的。

所以,随着所有新的高分辨率手机的发布,我认为使用硬编码值从长远来看不是一个好主意。

在各种屏幕和设备上进行测试后,我发现一个更好的方法是使用百分比。 获取decorView和你的应用内容之间的差异,然后检查这个差异的百分比。 从我得到的统计数据来看,大多数导航栏(无论大小、分辨率等)将占据屏幕的3%到5%。如果键盘是打开的,它会占用屏幕的47%到55%

作为结论,我的解决方案是检查是否差异超过10%,然后我假设它的键盘打开。

这个解决方案可能会重新打开键盘,但它是有效的。

InputMethodManager inputManager = ( (InputMethodManager) this.getSystemService(Context.INPUT_METHOD_SERVICE) );


private boolean isKeyboardShowing() {


boolean isKeyboardShowing = inputManager.hideSoftInputFromWindow(irrelevantView.getWindowToken(), 0);
if (isKeyboardShowing) {
inputManager.showSoftInput(this.getCurrentFocus(), 0);
}
return isKeyboardShowing;
}

我知道这是一篇老文章,但我认为这是我所知道的最简单的方法,我的测试设备是Nexus 5。我还没有在其他设备上尝试过。希望其他人会分享他们的方法,如果他们发现我的代码不好:)

public static boolean isKeyboardShown(Context context, View view) {
if (context == null || view == null) {
return false;
}
InputMethodManager imm = (InputMethodManager) context
.getSystemService(Context.INPUT_METHOD_SERVICE);
return imm.hideSoftInputFromWindow(view.getWindowToken(), 0);
}

imm。hideSoftInputFromWindow返回布尔值。

谢谢,

if (keyopen())
{
InputMethodManager imm = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE);
imm.toggleSoftInput(InputMethodManager.HIDE_IMPLICIT_ONLY,0);
}

上面的函数是我用来检查键盘是否可见的。如果是,那我就关上。

下面显示了所需的两种方法。

首先,在onCreate中定义可工作的窗口高度。

protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);


//  add to onCreate method
Rect rectgle= new Rect();
Window window= getWindow();
window.getDecorView().getWindowVisibleDisplayFrame(rectgle);
sheight= rectgle.bottom;
//


}

然后,添加一个布尔方法来获取该实例的窗口高度。如果它与原来的不匹配(假设您没有在此过程中更改它……),那么键盘是打开的。

public boolean keyopen()
{
Rect rectgle= new Rect();
Window window= getWindow();
window.getDecorView().getWindowVisibleDisplayFrame(rectgle);
int curheight= rectgle.bottom;


if (curheight!=sheight)
{
return true;
}
else
{
return false;
}
}

Frotz !

我知道如何准确地确定键盘是否隐藏。

public int getStatusBarHeight() {
int result = 0;
int resourceId = getResources().getIdentifier("status_bar_height", "dimen", "android");
if (resourceId > 0) {
result = getResources().getDimensionPixelSize(resourceId);
}
return result;
}


public int getNavigationBarHeight() {
int result = 0;
int resourceId = getResources().getIdentifier("navigation_bar_height", "dimen", "android");
if (resourceId > 0) {
result = getResources().getDimensionPixelSize(resourceId);
}
return result;
}


public boolean isKeyboardHidden() {
int delta = mRootView.getRootView().getHeight() - mRootView.getHeight() - getNavigationBarHeight() - getStatusBarHeight()
- getSupportActionBar().getHeight();
return delta <= 0;
}

这适用于平板电脑。当导航栏水平显示时。

如果键盘是可见的,你可以使用InputMethodManager.isActive方法返回true:

    InputMethodManager imm = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE);
imm.isActive();

你也可以看到键盘在一个特定的视图中是否处于活动状态:

    imm.isActive(View v);

Reuben Scratton和Kachi提供的解决方案似乎依赖于设备的像素密度,如果你有一个高密度的设备,即使按下键盘,高度差也可能超过100。围绕这一点的工作将是看到初始高度差(键盘向下),然后与当前的差异进行比较。

boolean isOpened = false;
int firstHeightDiff = -1;


public void setListenerToRootView(){
final View activityRootView = getActivity().getWindow().getDecorView().findViewById(android.R.id.content);
Rect r = new Rect();
activityRootView.getWindowVisibleDisplayFrame(r);
firstHeightDiff = activityRootView.getRootView().getHeight() - (r.bottom - r.top);
activityRootView.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
@Override
public void onGlobalLayout() {
if (isAdded()) {
Rect r = new Rect();
activityRootView.getWindowVisibleDisplayFrame(r);
int heightDiff = activityRootView.getRootView().getHeight() - (r.bottom - r.top);
isOpened = heightDiff>firstHeightDiff+100;
if (isAdded())
if(isOpened) {
//TODO stuff for when it is up
} else {
//TODO stuf for when it is down
}
}
}
});
}

这里有一个工作区,以了解软键盘是否可见。

  1. 检查系统中正在运行的业务 李ActivityManager.getRunningServices (max_count_of_services); < / >
  2. 从返回的ActivityManager。RunningServiceInfo实例,检查软键盘服务的clientCount值。
  3. 前面提到的clientCount每次都会增加,显示软键盘。例如,如果clientCount为 最初是1,当键盘显示时,它将是2
  4. 在键盘撤销时,clientCount递减。在本例中,它重置为1。

一些流行的键盘在它们的classname中有特定的关键字:

Google AOSP = IME
Swype = IME
Swiftkey = KeyboardService
Fleksy = keyboard
Adaptxt = IME (KPTAdaptxtIME)
Smart = Keyboard (SmartKeyboard)

从ActivityManager。RunningServiceInfo,在ClassNames中检查上述模式。同时,ActivityManager。RunningServiceInfo的clientPackage=android,表示键盘绑定到系统。

可以将上述信息进行严格的组合,以判断软键盘是否可见。

就计算机而言,这是永远的问题,但这个问题仍然令人难以置信地相关! 所以我把上面的答案进行了组合和改进
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 Rect r = new Rect();


@Override
public void onGlobalLayout() {
activityRootView.getWindowVisibleDisplayFrame(r);


int heightDiff = activityRootView.getRootView().getHeight() - (r.bottom - r.top);
boolean isOpen = heightDiff > 100;
if (isOpen == wasOpened) {
logDebug("Ignoring global layout change...");
return;
}


wasOpened = isOpen;
listener.onVisibilityChanged(isOpen);
}
});
}

这对我很管用。

当在viewpager中改变片段的方向时,我很难保持键盘状态。我不知道为什么,但它似乎是不稳定的,行为不同于标准的活动。

在这种情况下,要保持键盘状态,首先应该将android:windowSoftInputMode = "stateUnchanged"添加到AndroidManifest.xml。不过,您可能会注意到,这实际上并没有解决整个问题——如果在方向改变之前打开了键盘,那么键盘就不会为我打开。在其他所有情况下,这种行为似乎都是正确的。

然后,我们需要实现这里提到的解决方案之一。 我发现最干净的一个是George Maisuradze的——使用来自hideSoftInputFromWindow的布尔回调:

InputMethodManager imm = (InputMethodManager) getSystemService(Activity.INPUT_METHOD_SERVICE);
return imm.hideSoftInputFromWindow(mViewPager.getWindowToken(), 0);

我将这个值存储在Fragment的onSaveInstanceState方法中,并检索它onCreate。然后,我强制在onCreateView中显示键盘,如果它的值是true(如果键盘是可见的,那么在片段销毁之前隐藏它,它将返回true)。

试试这个:

final View activityRootView = getWindow().getDecorView().getRootView();
activityRootView.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.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.bottom - r.top);
if (heightDiff < activityRootView.getRootView().getHeight() / 4 ) { // if more than 100 pixels, its probably a keyboard...
// ... do something here ... \\
}
}
});

在了解了不同分辨率的一些问题后,我决定使用相对大小。正如我所注意到的,可见状态和隐藏状态之间的差异约为30%。所以我决定用0.3代替128 PX。

我添加了这个类监听器来通知任何变化。

这是我的版本

import android.app.*;
import android.graphics.*;
import android.view.*;


public class SoftKeyboardState {
public static final int HIDDEN = 0, VISIBLE = 1;
private OnKeyboardStateChangedListener listener;
private View decorView;


public SoftKeyboardState(Activity activity) {
this.decorView = activity.findViewById(android.R.id.content);
initKeyboardListener();
}


private void initKeyboardListener() {
decorView.getViewTreeObserver().addOnGlobalLayoutListener(
new ViewTreeObserver.OnGlobalLayoutListener(){
private final Rect windowVisibleDisplayFrame = new Rect();
private int lastVisibleDecorViewHeight;


@Override
public void onGlobalLayout() {
decorView.getWindowVisibleDisplayFrame(windowVisibleDisplayFrame);
final int visibleDecorViewHeight = windowVisibleDisplayFrame.height();


if (lastVisibleDecorViewHeight != 0) {
if ((lastVisibleDecorViewHeight > visibleDecorViewHeight) && (lastVisibleDecorViewHeight / visibleDecorViewHeight >= 0.3f)) {
// visible
if (listener != null)listener.onKeyboardStateChanged(VISIBLE);
} else if ((lastVisibleDecorViewHeight < visibleDecorViewHeight) && (visibleDecorViewHeight / lastVisibleDecorViewHeight >= 0.3f)) {
// hidden
if (listener != null)listener.onKeyboardStateChanged(HIDDEN);
}
}
lastVisibleDecorViewHeight = visibleDecorViewHeight;
}
});
}


public void setOnKeyboardStateChangedListener(OnKeyboardStateChangedListener listener) {
this.listener = listener;
}


public interface OnKeyboardStateChangedListener {
public void onKeyboardStateChanged(int state);
}
}

系统嵌入也有解决方案,但它只适用于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值来改变。

这里有一个隐藏的方法可以帮助实现这一点,InputMethodManager.getInputMethodWindowVisibleHeight。但我不知道为什么要藏起来。

import android.content.Context
import android.os.Handler
import android.view.inputmethod.InputMethodManager


class SoftKeyboardStateWatcher(private val ctx: Context) {
companion object {
private const val DELAY = 10L
}


private val handler = Handler()
private var isSoftKeyboardOpened: Boolean = false


private val height: Int
get() {
val imm = ctx.getSystemService(Context.INPUT_METHOD_SERVICE) as InputMethodManager
val method = imm.javaClass.getMethod("getInputMethodWindowVisibleHeight")
method.isAccessible = true
return method.invoke(imm) as Int
}


private val task: Runnable by lazy {
Runnable {
start()
if (!isSoftKeyboardOpened && height > 0) {
isSoftKeyboardOpened = true
notifyOnSoftKeyboardOpened(height)
} else if (isSoftKeyboardOpened && height == 0) {
isSoftKeyboardOpened = false
notifyOnSoftKeyboardClosed()
}
}
}


var listener: SoftKeyboardStateListener? = null


interface SoftKeyboardStateListener {
fun onSoftKeyboardOpened(keyboardHeightInPx: Int)
fun onSoftKeyboardClosed()
}


fun start() {
handler.postDelayed(task, DELAY)
}


fun stop() {
handler.postDelayed({
if (!isSoftKeyboardOpened) handler.removeCallbacks(task)
}, DELAY * 10)
}


private fun notifyOnSoftKeyboardOpened(keyboardHeightInPx: Int) {
listener?.onSoftKeyboardOpened(keyboardHeightInPx)
}


private fun notifyOnSoftKeyboardClosed() {
listener?.onSoftKeyboardClosed()
}
}

这段代码工作得很好

根视图使用这个类:

public class KeyboardConstraintLayout extends ConstraintLayout {


private KeyboardListener keyboardListener;
private EditText targetEditText;
private int minKeyboardHeight;
private boolean isShow;


public KeyboardConstraintLayout(Context context) {
super(context);
minKeyboardHeight = getResources().getDimensionPixelSize(R.dimen.keyboard_min_height);
}


public KeyboardConstraintLayout(Context context, AttributeSet attrs) {
super(context, attrs);
minKeyboardHeight = getResources().getDimensionPixelSize(R.dimen.keyboard_min_height);
}


public KeyboardConstraintLayout(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
minKeyboardHeight = getResources().getDimensionPixelSize(R.dimen.keyboard_min_height);
}


@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
if (!isInEditMode()) {
Activity activity = (Activity) getContext();
@SuppressLint("DrawAllocation")
Rect rect = new Rect();
getWindowVisibleDisplayFrame(rect);


int statusBarHeight = rect.top;
int keyboardHeight = activity.getWindowManager().getDefaultDisplay().getHeight() - (rect.bottom - rect.top) - statusBarHeight;


if (keyboardListener != null && targetEditText != null && targetEditText.isFocused()) {
if (keyboardHeight > minKeyboardHeight) {
if (!isShow) {
isShow = true;
keyboardListener.onKeyboardVisibility(true);
}
}else {
if (isShow) {
isShow = false;
keyboardListener.onKeyboardVisibility(false);
}
}
}
}
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
}


public boolean isShowKeyboard() {
return isShow;
}


public void setKeyboardListener(EditText targetEditText, KeyboardListener keyboardListener) {
this.targetEditText = targetEditText;
this.keyboardListener = keyboardListener;
}


public interface KeyboardListener {
void onKeyboardVisibility (boolean isVisible);
}

在activity或fragment中设置键盘监听器:

    rootLayout.setKeyboardListener(targetEditText, new KeyboardConstraintLayout.KeyboardListener() {
@Override
public void onKeyboardVisibility(boolean isVisible) {


}
});

也许这对你有帮助:

InputMethodManager imm = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE);
imm.toggleSoftInput(InputMethodManager.SHOW_FORCED, 0);

这里99%的解决方案是基于IME窗口大小的概率,每个这样的解决方案都是一个sh…价值!

因为:

  1. 覆盖-来自用户应用程序或系统应用程序
  2. IME没有最小尺寸,它可以占用100%的窗口大小,并且可以像开发人员实现的想象那样薄:)
  3. 模态窗口/多窗口
  4. 更多的人不了解IPC(例如:外部窗口或其内容检测)

所以猜它是输入法总是错误的-不要猜,一定要确定!!

@kevin-du是目前最好的解决方案,因为它的查询IMM的IME高度-但它说的方法是隐藏的API,所以使用它可能是危险的方式得到错误的“假阴性结果”;-错误的开发使用。

参考@TacB0sS的回答,我在Kotlin中开发了一个类。希望这对你有所帮助。如果需要改进,请告诉我。

class KeyboardVisibilityObserver(val layRootContainer: View?, val keyboardVisibilityListener: KeyboardVisibilityListener?) {
var isKeyboardOpen = false
private set


private var keyBoardObserver = object : ViewTreeObserver.OnGlobalLayoutListener {


private val DefaultKeyboardDP = 100


// Lollipop includes button bar in the root. Add height of button bar (48dp) to maxDiff
private val EstimatedKeyboardDP = DefaultKeyboardDP + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) 48 else 0


private val r = Rect()


override fun onGlobalLayout() {
if (layRootContainer != null) {
// Convert the dp to pixels.
val estimatedKeyboardHeight = TypedValue
.applyDimension(TypedValue.COMPLEX_UNIT_DIP, EstimatedKeyboardDP.toFloat(), layRootContainer.resources.displayMetrics).toInt()


// Conclude whether the keyboard is shown or not.
layRootContainer.getWindowVisibleDisplayFrame(r)
val heightDiff = layRootContainer.rootView.height - (r.bottom - r.top)
val isShown = heightDiff >= estimatedKeyboardHeight


if (isShown == isKeyboardOpen) {
//  Log.d("Keyboard state", "Ignoring global layout change...");
return
}


isKeyboardOpen = isShown


keyboardVisibilityListener?.onKeyboardVisibilityChanged(isKeyboardOpen)
}
}
}


init {
layRootContainer?.viewTreeObserver?.addOnGlobalLayoutListener(keyBoardObserver)
}


// call this in onDestroy
fun removeObserver(){
layRootContainer?.viewTreeObserver?.removeOnGlobalLayoutListener(keyBoardObserver)
}


interface KeyboardVisibilityListener {
fun onKeyboardVisibilityChanged(isKeyboardOpen: Boolean)
}
}
哇,我们有好消息Android极客们。是时候向旧的方式说再见了。 首先,我将添加正式发布说明来阅读和了解更多关于这些方法/类的信息,然后我们将看到这些神奇的方法/ classes

注意:不要将这些添加到你的发布应用中,直到这些类/方法发布

如何检查键盘可见性

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
}

根据doc.. # EYZ0 < / p >

检查释放说明.. # EYZ0 < / p >

要获得当前键盘可见性,可以使用getRootWindowInsets,然后调用isVisible()函数,传入IME类型。

val windowinsetscompat = ViewCompat.getRootWindowInsets(view)
val imeVisible = windowinsetscompat.isVisible(Type.ime())

还有一个监听器用于更改OnApplyWindowInsetsListener

ViewCompat.setOnApplyWindowInsetsListener(view) { v, insets ->
val imeVisible = insets.isVisible(Type.ime())
}

除了正确的答案,我必须在onCreateView的末尾添加这个,当使用一个片段内的webview。

getActivity().getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_ADJUST_PAN);

可能是因为我在一个片段中运行Webview,或者可能是API 30上的一个新行为,我的问题是,即使显示键盘,片段的高度也从未改变。

所以对于Fragment,整个代码应该是

@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View view = super.onCreateView(inflater, container, savedInstanceState);
//mWebView.postUrl("https://www.google.com/");
final View activityRootView = view;
layoutListener = new ViewTreeObserver.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);
// This variable was created only for Debug purposes and
// to see the height change when clicking on a field inside mWebView
int screenHeight = activityRootView.getRootView().getHeight();
Log.d("onGlobalLayout", "rect: " + r.toString());
Log.d("onGlobalLayout", "screenHeight: " + screenHeight);


//The difference on the heights from bottom to top and on the root height
int heightDiff = screenHeight - (r.bottom - r.top);
Log.d("onGlobalLayout", "heightDiff: " + heightDiff);


//I suggest to put 250 on resources to have better order
float dpx = dpToPx(getActivity(), 250);


if (previousHeightDiff != heightDiff) {
if (heightDiff > dpx) {
isSoftKeyboardPresent = true;
} else {
isSoftKeyboardPresent = false;
}
previousHeightDiff = heightDiff;
}
}
};
activityRootView.getViewTreeObserver().addOnGlobalLayoutListener(layoutListener);
getActivity().getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_ADJUST_PAN);
return view;
}


private static float dpToPx(Context context, float valueInDp) {
DisplayMetrics metrics = context.getResources().getDisplayMetrics();
return TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, valueInDp, metrics);
}

这可能不适合生产环境,因为它会打开键盘。注意,类似函数返回的布尔值在API中没有指定,因此是不可靠的。参考这里的文档…

https://developer.android.com/reference/android/view/inputmethod/InputMethodManager#showSoftInput(android.view.View,%20int,%20android.os.ResultReceiver)

public boolean showSoftInput (View view,
int flags,
ResultReceiver resultReceiver)

注意,这个方法接受一个ResultReceiver。它可以得到结果:result_unchanged_shows, RESULT_UNCHANGED_HIDDEN, result_shows,或RESULT_HIDDEN。如果得到result_unchanged_show,则键盘是可见的。如果你需要它保持关闭如果它已经关闭了,你需要关闭它。

视图#setOnApplyWindowInsetsListener可以用来获得窗口insets回调

public void setOnApplyWindowInsetsListener(OnApplyWindowInsetsListener listener) {
getListenerInfo().mOnApplyWindowInsetsListener = listener;
}


//OnApplyWindowInsetsListener
public WindowInsets onApplyWindowInsets(View v, WindowInsets insets);

boolean keyboardVisible = insets.isVisible(WindowInsets.Type.ime())可以给出可见状态。