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


当前回答

所以,在花了很长一段时间摆弄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()是一个anko函数,转换dpToPx:

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

试试这个,对我很有效

InputMethodManager imm = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE);
    if (imm.isActive())
        //Keyboard is active

试试这个:

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

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

它使用活动的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
}

其中更新边距

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
}

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

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