我如何检测软件键盘在Android设备上是否可见?

Android系统中是否有一种方法可以检测软件(也就是Android。“软”)键盘在屏幕上可见吗?

333860 次浏览

我也遇到过类似的问题。我需要对屏幕上的Enter按钮做出反应(这隐藏了键盘)。在这种情况下,你可以订阅键盘打开的文本视图的OnEditorAction -如果你有多个可编辑框,那么就订阅所有的文本视图。

在你的活动中,你可以完全控制键盘,所以如果你监听所有打开和关闭事件,你就不会面临键盘是否打开的问题。

没有直接的方法-参见http://groups.google.com/group/android-platform/browse_thread/thread/1728f26f2334c060/5e4910f0d9eb898a,其中来自Android团队的Dianne Hackborn已经回复。但是,您可以通过检查#onMeasure中窗口大小是否改变来间接检测它。看到如何检查软件键盘在Android的可见性?

试试这个:

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


if (imm.isAcceptingText()) {
writeToLog("Software Keyboard was shown");
} else {
writeToLog("Software Keyboard was not shown");
}

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

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

我通过设置一个GlobalLayoutListener来做到这一点,如下所示:

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

你可以参考这个答案——https://stackoverflow.com/a/24105062/3629912

每次都很管用。

adb shell dumpsys window InputMethod | grep "mHasSurface"

如果软件键盘可见,它将返回true。

这对我很有用。也许这总是最好的方法适用于所有版本

这将是有效的,使键盘可见性的属性,并观察这个变化延迟,因为onGlobalLayout方法调用多次。此外,检查设备旋转和windowSoftInputMode不是adjustNothing也是很好的。

boolean isKeyboardShowing = false;
void onKeyboardVisibilityChanged(boolean opened) {
print("keyboard " + opened);
}


// ContentView is the root view of the layout of this activity/fragment
contentView.getViewTreeObserver().addOnGlobalLayoutListener(
new ViewTreeObserver.OnGlobalLayoutListener() {
@Override
public void onGlobalLayout() {


Rect r = new Rect();
contentView.getWindowVisibleDisplayFrame(r);
int screenHeight = contentView.getRootView().getHeight();


// r.bottom is the position above soft keypad or device button.
// if keypad is shown, the r.bottom is smaller than that before.
int keypadHeight = screenHeight - r.bottom;


Log.d(TAG, "keypadHeight = " + keypadHeight);


if (keypadHeight > screenHeight * 0.15) { // 0.15 ratio is perhaps enough to determine keypad height.
// keyboard is opened
if (!isKeyboardShowing) {
isKeyboardShowing = true
onKeyboardVisibilityChanged(true)
}
}
else {
// keyboard is closed
if (isKeyboardShowing) {
isKeyboardShowing = false
onKeyboardVisibilityChanged(false)
}
}
}
});

有一个直接的方法来找出这个。并且,它不需要改变布局 所以它也可以在沉浸式全屏模式下工作 但不幸的是,它并不适用于所有设备。所以你必须用你的设备测试它。

窍门是您尝试隐藏或显示软键盘,并捕获该尝试的结果 如果它工作正确,那么键盘不会真正显示或隐藏。我们只是请求状态。

为了保持最新,你只需重复这个操作,例如每200毫秒,使用Handler.

下面的实现只做了一次检查 如果要进行多次检查,则应该启用所有(_keyboardVisible)测试

public interface OnKeyboardShowHide
{
void    onShowKeyboard( Object param );
void    onHideKeyboard( Object param );
}


private static Handler      _keyboardHandler    = new Handler();
private boolean             _keyboardVisible    = false;
private OnKeyboardShowHide  _keyboardCallback;
private Object              _keyboardCallbackParam;


public void start( OnKeyboardShowHide callback, Object callbackParam )
{
_keyboardCallback      = callback;
_keyboardCallbackParam = callbackParam;
//
View view = getCurrentFocus();
if (view != null)
{
InputMethodManager imm = (InputMethodManager) getSystemService( Activity.INPUT_METHOD_SERVICE );
imm.hideSoftInputFromWindow( view.getWindowToken(), InputMethodManager.HIDE_IMPLICIT_ONLY, _keyboardResultReceiver );
imm.showSoftInput( view, InputMethodManager.SHOW_IMPLICIT, _keyboardResultReceiver );
}
else // if (_keyboardVisible)
{
_keyboardVisible = false;
_keyboardCallback.onHideKeyboard( _keyboardCallbackParam );
}
}


private ResultReceiver      _keyboardResultReceiver = new ResultReceiver( _keyboardHandler )
{
@Override
protected void onReceiveResult( int resultCode, Bundle resultData )
{
switch (resultCode)
{
case InputMethodManager.RESULT_SHOWN :
case InputMethodManager.RESULT_UNCHANGED_SHOWN :
// if (!_keyboardVisible)
{
_keyboardVisible = true;
_keyboardCallback.onShowKeyboard( _keyboardCallbackParam );
}
break;
case InputMethodManager.RESULT_HIDDEN :
case InputMethodManager.RESULT_UNCHANGED_HIDDEN :
// if (_keyboardVisible)
{
_keyboardVisible = false;
_keyboardCallback.onHideKeyboard( _keyboardCallbackParam );
}
break;
}
}
};

试试这段代码,它真的工作,如果keyboardshow是显示的,那么这个函数返回真值....

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

我是这样做的,但只有当你的目标是关闭/打开键盘时,它才有意义。

关闭示例:(检查键盘是否已经关闭,如果没有关闭)

imm.showSoftInput(etSearch, InputMethodManager.HIDE_IMPLICIT_ONLY, new ResultReceiver(null) {
@Override
protected void onReceiveResult(int resultCode, Bundle resultData) {
super.onReceiveResult(resultCode, resultData);
if (resultCode != InputMethodManager.RESULT_UNCHANGED_HIDDEN)
imm.toggleSoftInput(InputMethodManager.SHOW_FORCED, 0);
}
});

我使用这个作为基础:https://rogerkeays.com/how-to-check-if-the-software-keyboard-is-shown-in-android

/**
* To capture the result of IMM hide/show soft keyboard
*/
public class IMMResult extends ResultReceiver {
public int result = -1;
public IMMResult() {
super(null);
}


@Override
public void onReceiveResult(int r, Bundle data) {
result = r;
}


// poll result value for up to 500 milliseconds
public int getResult() {
try {
int sleep = 0;
while (result == -1 && sleep < 500) {
Thread.sleep(100);
sleep += 100;
}
} catch (InterruptedException e) {
Log.e("IMMResult", e.getMessage());
}
return result;
}
}

然后写了这个方法:

public boolean isSoftKeyboardShown(InputMethodManager imm, View v) {
    

IMMResult result = new IMMResult();
int res;
    

imm.showSoftInput(v, 0, result);


// if keyboard doesn't change, handle the keypress
res = result.getResult();
if (res == InputMethodManager.RESULT_UNCHANGED_SHOWN ||
res == InputMethodManager.RESULT_UNCHANGED_HIDDEN) {


return true;
}
else
return false;


}

然后你可以用它来测试所有可能打开软键盘的字段(EditText, AutoCompleteTextView等):

    InputMethodManager imm = (InputMethodManager) getActivity().getSystemService(Context.INPUT_METHOD_SERVICE);
if(isSoftKeyboardShown(imm, editText1) | isSoftKeyboardShown(imm, autocompletetextview1))
//close the softkeyboard
imm.toggleSoftInput(InputMethodManager.SHOW_FORCED, 0);

当然不是一个理想的解决方案,但它可以完成工作。

在Android中,你可以通过ADB shell进行检测。我写下并使用了这个方法:

{
JSch jsch = new JSch();
try {
Session session = jsch.getSession("<userName>", "<IP>", 22);
session.setPassword("<Password>");
Properties config = new Properties();
config.put("StrictHostKeyChecking", "no");
session.setConfig(config);
session.connect();


ChannelExec channel = (ChannelExec)session.openChannel("exec");
BufferedReader in = new BufferedReader(new
InputStreamReader(channel.getInputStream()));
channel.setCommand("C:/Android/android-sdk/platform-tools/adb shell dumpsys window
InputMethod | findstr \"mHasSurface\"");
channel.connect();


String msg = null;
String msg2 = " mHasSurface=true";


while ((msg = in.readLine()) != null) {
Boolean isContain = msg.contains(msg2);
log.info(isContain);
if (isContain){
log.info("Hiding keyboard...");
driver.hideKeyboard();
}
else {
log.info("No need to hide keyboard.");
}
}


channel.disconnect();
session.disconnect();


} catch (JSchException | IOException | InterruptedException e) {
e.printStackTrace();
}
}
}

对于我所需要的需求来说,这要简单得多。希望这能有所帮助:

关于MainActivity:

public void dismissKeyboard(){
InputMethodManager imm =(InputMethodManager)this.getSystemService(Context.INPUT_METHOD_SERVICE);
imm.hideSoftInputFromWindow(mSearchBox.getWindowToken(), 0);
mKeyboardStatus = false;
}


public void showKeyboard(){
InputMethodManager imm =(InputMethodManager)this.getSystemService(Context.INPUT_METHOD_SERVICE);
imm.toggleSoftInput(InputMethodManager.SHOW_FORCED, InputMethodManager.HIDE_IMPLICIT_ONLY);
mKeyboardStatus = true;
}


private boolean isKeyboardActive(){
return mKeyboardStatus;
}

mKeyboardStatus的默认基元布尔值将初始化为

然后检查如下值,并执行相应的操作:

 mSearchBox.requestFocus();
if(!isKeyboardActive()){
showKeyboard();
}else{
dismissKeyboard();
}
在我的情况下,我只有一个EditText来管理我的布局,所以我想出了解决方案。 它工作得很好,基本上它是一个自定义的EditText,它监听焦点,并在焦点改变或按下back/done按钮时发送本地广播。 为了工作,你需要在你的布局中使用android:focusable="true"android:focusableInTouchMode="true"放置一个虚拟的View,因为当你调用clearFocus()时,焦点将被重新分配给第一个可聚焦的视图。 虚拟视图示例:

<View
android:layout_width="1dp"
android:layout_height="1dp"
android:focusable="true"
android:focusableInTouchMode="true"/>

额外的信息

检测布局变化差异的解决方案效果不太好,因为它强烈依赖于屏幕密度,因为100px在某些设备上可能很多,而在其他一些设备上可能没有,你可能会得到假阳性。 不同的厂商有不同的键盘

非常容易

1. 把id放在根视图上

rootView只是一个指向我的根视图的视图,在这种情况下是relative layout:

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:id="@+id/addresses_confirm_root_view"
android:background="@color/WHITE_CLR">

2. 在Activity中初始化根视图:

RelativeLayout rootView = (RelativeLayout) findViewById(R.id.addresses_confirm_root_view);

3.使用getViewTreeObserver()检测键盘是否打开或关闭

    rootView.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
@Override
public void onGlobalLayout() {
int heightDiff = rootView.getRootView().getHeight() - rootView.getHeight();
                

if (heightDiff > 100) { // Value should be less than keyboard's height
Log.e("MyActivity", "keyboard opened");
} else {
Log.e("MyActivity", "keyboard closed");
}
}
});
final View activityRootView = findViewById(R.id.rootlayout);
activityRootView.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
@Override
public void onGlobalLayout() {


Rect r = new Rect();
activityRootView.getWindowVisibleDisplayFrame(r);


int screenHeight = activityRootView.getRootView().getHeight();
Log.e("screenHeight", String.valueOf(screenHeight));
int heightDiff = screenHeight - (r.bottom - r.top);
Log.e("heightDiff", String.valueOf(heightDiff));
boolean visible = heightDiff > screenHeight / 3;
Log.e("visible", String.valueOf(visible));
if (visible) {
Toast.makeText(LabRegister.this, "I am here 1", Toast.LENGTH_SHORT).show();
} else {
Toast.makeText(LabRegister.this, "I am here 2", Toast.LENGTH_SHORT).show();
}
}
});

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

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

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

  1. 谷歌AOSP = IME
  2. Swype =输入法
  3. Swiftkey = KeyboardService
  4. Fleksy =键盘
  5. Adaptxt = IME (KPTAdaptxtIME)
  6. 智能=键盘(SmartKeyboard)
从ActivityManager < p >。RunningServiceInfo,在ClassNames中检查上述模式。 同时,ActivityManager。RunningServiceInfo的clientPackage=android,表示键盘绑定到系统

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

A可能会使用:

@Override
public void onConfigurationChanged(Configuration newConfig) {
super.onConfigurationChanged(newConfig);


Log.d(
getClass().getSimpleName(),
String.format("conf: %s", newConfig));


if (newConfig.hardKeyboardHidden != hardKeyboardHidden) {
onHardwareKeyboardChange(newConfig.hardKeyboardHidden);


hardKeyboardHidden = newConfig.hardKeyboardHidden;
}


if (newConfig.keyboardHidden != keyboardHidden) {
onKeyboardChange(newConfig.keyboardHidden);


keyboardHidden = newConfig.hardKeyboardHidden;
}


}


public static final int KEYBOARDHIDDEN_UNDEFINED = 0;
public static final int KEYBOARDHIDDEN_NO = 1;
public static final int KEYBOARDHIDDEN_YES = 2;
public static final int KEYBOARDHIDDEN_SOFT = 3;


//todo
private void onKeyboardChange(int keyboardHidden) {


}


//todo
private void onHardwareKeyboardChange(int hardKeyboardHidden) {


}

我创建了一个可以用于此目的的简单类:https://github.com/ravindu1024/android-keyboardlistener。只需将其复制到您的项目中,并按如下方式使用:

KeyboardUtils.addKeyboardToggleListener(this, new KeyboardUtils.SoftKeyboardToggleListener()
{
@Override
public void onToggleSoftKeyboard(boolean isVisible)
{
Log.d("keyboard", "keyboard visible: "+isVisible);
}
});
@iWantScala的答案是伟大的,但不适合我
rootView.getRootView().getHeight()总是有相同的值

一种方法是定义两个变量

private int maxRootViewHeight = 0;
private int currentRootViewHeight = 0;

添加全局监听器

rootView.getViewTreeObserver()
.addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
@Override
public void onGlobalLayout() {
currentRootViewHeight = rootView.getHeight();
if (currentRootViewHeight > maxRootViewHeight) {
maxRootViewHeight = currentRootViewHeight;
}
}
});

然后检查

if (currentRootViewHeight >= maxRootViewHeight) {
// Keyboard is hidden
} else {
// Keyboard is shown
}

工作正常

所以,在花了很长一段时间摆弄accesessibilityservices、窗口嵌入、屏幕高度检测等之后,我想我找到了一种方法来做到这一点。

免责声明:它在Android中使用了一个隐藏的方法,这意味着它可能不一致。然而,在我的测试中,它似乎有效。

方法是InputMethodManager # getInputMethodWindowVisibleHeight (),它自Lollipop(5.0)以来就存在了。

调用它将返回当前键盘的高度(以像素为单位)。理论上,键盘不应该是0像素高,所以我做了一个简单的高度检查(在Kotlin中):

val imm by lazy { context.getSystemService(Context.INPUT_METHOD_SERVICE) as InputMethodManager }
if (imm.inputMethodWindowVisibleHeight > 0) {
//keyboard is shown
else {
//keyboard is hidden
}

当我调用隐藏方法时,我使用Android隐藏API来避免反射(对于我开发的应用程序,我经常这样做,这些应用程序主要是hack /tuner应用程序),但这应该也可以通过反射实现:

val imm by lazy { context.getSystemService(Context.INPUT_METHOD_SERVICE) as InputMethodManager }
val windowHeightMethod = InputMethodManager::class.java.getMethod("getInputMethodWindowVisibleHeight")
val height = windowHeightMethod.invoke(imm) as Int
//use the height val in your logic

这应该工作,如果你需要检查键盘状态:

fun Activity.isKeyboardOpened(): Boolean {
val r = Rect()


val activityRoot = getActivityRoot()
val visibleThreshold = dip(UiUtils.KEYBOARD_VISIBLE_THRESHOLD_DP)


activityRoot.getWindowVisibleDisplayFrame(r)


val heightDiff = activityRoot.rootView.height - r.height()


return heightDiff > visibleThreshold;
}


fun Activity.getActivityRoot(): View {
return (findViewById<ViewGroup>(android.R.id.content)).getChildAt(0);
}

UiUtils.KEYBOARD_VISIBLE_THRESHOLD_DP = 100, dip()是一个转换dpToPx的anko函数:

fun dip(value: Int): Int {
return (value * Resources.getSystem().displayMetrics.density).toInt()
}

如你所知,android软件的键盘只有在有可能的输入事件时才可见。换句话说,只有当EditText被聚焦时,键盘才可见。这意味着你可以通过使用OnFocusChangeListener来获取键盘是否可见。

//Declare this Globally


public boolean isKeyBoardVisible = false;


//In OnCreate *[For Activity]*, OnCreateView *[For Fragment]*


text_send.setOnFocusChangeListener(new View.OnFocusChangeListener() {


@Override
public void onFocusChange(View v, boolean hasFocus) {
if(hasFocus)
isKeyBoardVisible = true;
else
isKeyBoardVisible = false;
}
});

现在你可以在类中的任何地方使用isKeyBoardVisible变量来获取键盘是否打开。这对我来说很有效。

注意:当使用InputMethodManager以编程方式打开键盘时,此过程不起作用,因为这不会调用OnFocusChangeListener。

我把答案转换为kotlin,希望这对kotlin用户有帮助。

private fun checkKeyboardVisibility() {
var isKeyboardShowing = false


binding.coordinator.viewTreeObserver.addOnGlobalLayoutListener {
val r = Rect()
binding.coordinator.getWindowVisibleDisplayFrame(r)
val screenHeight = binding.coordinator.rootView.height


// r.bottom is the position above soft keypad or device button.
// if keypad is shown, the r.bottom is smaller than that before.
val keypadHeight = screenHeight - r.bottom




if (keypadHeight > screenHeight * 0.15) { // 0.15 ratio is perhaps enough to determine keypad height.
// keyboard is opened
if (!isKeyboardShowing) {
isKeyboardShowing = true


}
} else {
// keyboard is closed
if (isKeyboardShowing) {
isKeyboardShowing = false


}
}
}
}

我写了样本

这个存储库可以帮助检测键盘状态,而无需假设“键盘应该超过屏幕的X部分”。

 ></a></p>


<p><a href= ></a></p></div>
                                                                            </div>
                                </div>
                            </div>
                        </div>
                                                <div class=

现在终于可以直接从基于Kotlin的Android R开始了。

 val imeInsets = requireView().rootWindowInsets.isVisible(WindowsInsetsCompat.Type.ime())
if (imeInsets) {
//Ime is visible
//Lets move our view by the height of the IME
view.translationX = imeInsets.bottom }

如果你的应用程序支持AndroidR的api,那么你可以使用下面的方法。

In kotlin :
var imeInsets = view.rootWindowInsets.getInsets(Type.ime())
if (imeInsets.isVisible) {
view.translationX = imeInsets.bottom
}

注意:这只适用于AndroidR和以下的android版本需要遵循一些其他的答案,否则我会更新它。

它使用活动的adjustNothing标志和使用生命周期事件。还有Kotlin:

/**
* This class uses a PopupWindow to calculate the window height when the floating keyboard is opened and closed
*
* @param activity The parent activity
*  The root activity that uses this KeyboardManager
*/
class KeyboardManager(private val activity: AppCompatActivity) : PopupWindow(activity), LifecycleObserver {


private var observerList = mutableListOf<((keyboardTop: Int) -> Unit)>()


/** The last value of keyboardTop */
private var keyboardTop: Int = 0


/** The view that is used to calculate the keyboard top  */
private val popupView: View?


/** The parent view  */
private var parentView: View


var isKeyboardShown = false
private set


/**
* Create transparent view which will be stretched over to the full screen
*/
private fun createFullScreenView(): View {
val view = LinearLayout(activity)
view.layoutParams = LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,
ViewGroup.LayoutParams.MATCH_PARENT)
view.background = ColorDrawable(Color.TRANSPARENT)
return view
}


init {
this.popupView = createFullScreenView()
contentView = popupView


softInputMode = LayoutParams.SOFT_INPUT_ADJUST_RESIZE or LayoutParams.SOFT_INPUT_STATE_ALWAYS_VISIBLE
inputMethodMode = INPUT_METHOD_NEEDED


parentView = activity.findViewById(android.R.id.content)


width = 0
height = LayoutParams.MATCH_PARENT


popupView.viewTreeObserver.addOnGlobalLayoutListener {
val rect = Rect()
popupView.getWindowVisibleDisplayFrame(rect)


val keyboardTop = rect.bottom
if (this.keyboardTop != keyboardTop) {
isKeyboardShown = keyboardTop < this.keyboardTop
this.keyboardTop = keyboardTop
observerList.forEach { it(keyboardTop) }
}
}
activity.lifecycle.addObserver(this)
}


/**
* This must be called after the onResume of the Activity or inside view.post { } .
* PopupWindows are not allowed to be registered before the onResume has finished
* of the Activity
*/
@OnLifecycleEvent(Lifecycle.Event.ON_RESUME)
fun start() {
parentView.post {
if (!isShowing && parentView.windowToken != null) {
setBackgroundDrawable(ColorDrawable(0))
showAtLocation(parentView, Gravity.NO_GRAVITY, 0, 0)
}
}
}


/**
* This manager will not be used anymore
*/
@OnLifecycleEvent(Lifecycle.Event.ON_DESTROY)
fun close() {
activity.lifecycle.removeObserver(this)
observerList.clear()
dismiss()
}


/**
* Set the keyboard top observer. The observer will be notified when the keyboard top has changed.
* For example when the keyboard is opened or closed
*
* @param observer The observer to be added to this provider
*/
fun registerKeyboardTopObserver(observer: (keyboardTop: Int) -> Unit) {
observerList.add(observer)
}
}

有用的方法,保持视图始终高于键盘

fun KeyboardManager.updateBottomMarginIfKeyboardShown(
view: View,
activity: AppCompatActivity,
// marginBottom of view when keyboard is hide
marginBottomHideKeyboard: Int,
// marginBottom of view when keybouard is shown
marginBottomShowKeyboard: Int
) {
registerKeyboardTopObserver { bottomKeyboard ->
val bottomView = ViewUtils.getFullViewBounds(view).bottom
val maxHeight = ScreenUtils.getFullScreenSize(activity.windowManager).y
// Check that view is within the window size
if (bottomView < maxHeight) {
if (bottomKeyboard < bottomView) {
ViewUtils.updateMargin(view, bottomMargin = bottomView - bottomKeyboard +
view.marginBottom + marginBottomShowKeyboard)
} else ViewUtils.updateMargin(view, bottomMargin = marginBottomHideKeyboard)
}
}
}

在getFullViewBounds

fun getLocationOnScreen(view: View): Point {
val location = IntArray(2)
view.getLocationOnScreen(location)
return Point(location[0], location[1])
}


fun getFullViewBounds(view: View): Rect {
val location = getLocationOnScreen(view)
return Rect(location.x, location.y, location.x + view.width,
location.y + view.height)
}

在getFullScreenSize

fun getFullScreenSize(wm: WindowManager? = null) =
getScreenSize(wm) { getRealSize(it) }


private fun getScreenSize(wm: WindowManager? = null, block: Display.(Point) -> Unit): Point {
val windowManager = wm ?: App.INSTANCE.getSystemService(Context.WINDOW_SERVICE)
as WindowManager
val point = Point()
windowManager.defaultDisplay.block(point)
return point
}

在updateMargin

fun updateMargin(
view: View,
leftMargin: Int? = null,
topMargin: Int? = null,
rightMargin: Int? = null,
bottomMargin: Int? = null
) {
val layoutParams = view.layoutParams as ViewGroup.MarginLayoutParams
if (leftMargin != null) layoutParams.leftMargin = leftMargin
if (topMargin != null) layoutParams.topMargin = topMargin
if (rightMargin != null) layoutParams.rightMargin = rightMargin
if (bottomMargin != null) layoutParams.bottomMargin = bottomMargin
view.layoutParams = layoutParams
}

使用Androidx核心版本1.5.0-alpha02中的新特性WindowInsetsCompat,你可以很容易地检查软键盘的可见性,如下所示

引用自reddit的评论

val View.keyboardIsVisible: Boolean
get() = WindowInsetsCompat
.toWindowInsetsCompat(rootWindowInsets)
.isVisible(WindowInsetsCompat.Type.ime())

一些关于向后兼容性的注释,引用自发布说明

新功能

WindowInsetsCompat api已更新为平台中的api Android 11。这包括新的ime()插入类型,它允许 检查屏幕上键盘的可见性和大小 关于ime()类型有一些注意事项,它在API 23+上工作非常可靠 当你的Activity使用adjustResize窗口软输入模式时。 如果你使用的是adjustPan模式,它应该工作可靠 返回API 14.

参考文献

一个基于@bohdan-oliynyk答案的更紧凑的Kotlin版本

private const val KEYBOARD_VISIBLE_THRESHOLD_DP = 100


fun Activity.isKeyboardOpen(): Boolean {
fun convertDpToPx(value: Int): Int =
(value * Resources.getSystem().displayMetrics.density).toInt()


val rootView = findViewById<View>(android.R.id.content)
val visibleThreshold = Rect()
rootView.getWindowVisibleDisplayFrame(visibleThreshold)
val heightDiff = rootView.height - visibleThreshold.height()


val accessibleValue = convertDpToPx(KEYBOARD_VISIBLE_THRESHOLD_DP)


return heightDiff > accessibleValue
}


fun Activity.isKeyboardClosed(): Boolean {
return isKeyboardOpen().not()
}
你可以从androidx。核心(版本1.5.0-rc01)中使用< >强WindowInsetsCompat < / >强。 此代码将从API 21及以上开始工作。Kotlin代码示例:

ViewCompat.setOnApplyWindowInsetsListener(root) { v, insets ->
val isKeyboardVisible = insets.isVisible(WindowInsetsCompat.Type.ime())
if (isKeyboardVisible) {
}
}

< >强根< / >强是Activity的根视图。

更新

今天我正在寻找如何检测键盘可见性。起初,这个代码不起作用。所以我不得不:

  1. 添加android:windowSoftInputMode="adjustResize"到我的AndroidManifest.xml文件:
xml
<activity android:name="com.soumicslabs.activitykt.StartActivity"
android:theme="@style/AccountKitTheme.Default"
android:configChanges="orientation|screenSize"
android:screenOrientation="portrait"
android:windowSoftInputMode="adjustResize"
/>
  1. 在你的活动中,设置WindowCompat.setDecorFitsSystemWindows(window, false),这告诉android我们想手动处理事情/不想使用系统默认值:
        val window = this.window
WindowCompat.setDecorFitsSystemWindows(window, false)  // <-- this tells android not to use system defaults, so we have to setup quite a lot of behaviors manually
  1. 最后,设置你onApplyWindowInsetsListener:
val callBack = OnApplyWindowInsetsListener { view, insets ->
val imeHeight = insets?.getInsets(WindowInsetsCompat.Type.ime())?.bottom?:0
Log.e("tag", "onKeyboardOpenOrClose imeHeight = $imeHeight")
// todo: logic
val isKeyboardVisible = insets.isVisible(WindowInsetsCompat.Type.ime())
if (isKeyboardVisible) {
// do something
}else{
// do something else
}
insets?: WindowInsetsCompat(null)
}


ViewCompat.setOnApplyWindowInsetsListener(mainContainer, callBack)

这对我很管用。

谢谢大家的回答,我是根据自己的情况想出来的

/**
* Add global layout listener to observe system keyboard visibility
*/
private void initObserverForSystemKeyboardVisibility() {
getRootView().getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
@Override
public void onGlobalLayout() {
//Add your own code here
Log.d("TEST_CODE", "isSystemKeyboardVisible:" + isSystemKeyboardVisible())
}
});
}




/**
* Check system keyboard visibility
* @return true if visible
*/
public boolean isSystemKeyboardVisible() {
try {
final InputMethodManager manager = (InputMethodManager) getContext().getSystemService(Context.INPUT_METHOD_SERVICE);
final Method windowHeightMethod = InputMethodManager.class.getMethod("getInputMethodWindowVisibleHeight");
final int height = (int) windowHeightMethod.invoke(manager);
return height > 0;
} catch (Exception e) {
return false;
}
}

试试这个,对我很有效

InputMethodManager imm = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE);
if (imm.isActive())
//Keyboard is active
private fun isKeyboardVisible(rootView: View) =
ViewCompat.getRootWindowInsets(rootView)!!.isVisible(WindowInsetsCompat.Type.ime())