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


当前回答

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

https://developer.android.com/reference/android/view/inputmethod/InputMethodManager showSoftInput (% 20 int, android.view.View % 20 android.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,则键盘是可见的。如果你需要它保持关闭如果它已经关闭了,你需要关闭它。

其他回答

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

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

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上的实现

这些解决方案都不适用于棒棒糖。在Lollipop 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毫秒,使用处理器。 您可以在这里找到一个实现:https://stackoverflow.com/a/27567074/2525452

有一个隐藏的方法可以帮助这个,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()
  }
}

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

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

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

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

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