如何在安卓系统中检查软键盘的可见性?

559

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


9
尽管Reuben Scratton的回答很好,但在平板电脑上似乎有问题。我将检查diff> 128替换为diff> screenHeight/3。 - kingston
2
Reuben Scratton的回答很好,但我需要KaChi的调整才能真正使用它。 - Cullan
2
为什么谷歌不为所有的键盘应用程序制作一个通用的内置方法? - 林果皞
5
我很在意这不是一个系统功能的事实。 - longi
1
这个API仍然缺失了10年,真是太疯狂了。非常高兴我已经远离Android了。 - Reuben Scratton
显示剩余7条评论
45个回答

1

这里99%的解决方案都基于IME窗口大小的概率,而每个这样的解决方案都不值得信赖!

因为:

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

因此,猜测IME总是错误的 - 不要猜测,要确保!!!

@kevin-du目前是最好的解决方案,因为它查询IMM以获取IME高度 - 但正如所说的那样,该方法是隐藏的API,因此使用它可能会导致错误的“假阴性结果” - 因为错误的开发者使用。


0

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

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

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

我不知道这是否是一个错误,我已经尝试了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
    }
 }
}); 

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

0

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

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

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


0

这里有一个解决方法,可以知道软键盘是否可见。

  1. 使用 ActivityManager.getRunningServices(max_count_of_services) 检查系统上运行的服务。
  2. 从返回的 ActivityManager.RunningServiceInfo 实例中,检查 clientCount 值以获取软键盘服务。
  3. 上述 clientCount 将在每次显示软键盘时递增。例如,如果 clientCount 最初为 1,则在显示键盘时将变为 2。
  4. 在键盘消失时,clientCount 会递减。在这种情况下,它将重置为 1。

一些流行的键盘在其 classNames 中具有某些关键字:

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

从ActivityManager.RunningServiceInfo中,在ClassNames中检查上述模式。此外,ActivityManager.RunningServiceInfo的clientPackage=android,表示键盘绑定到系统。
可以将上述信息结合起来,严格地找出软键盘是否可见。

0

View#setOnApplyWindowInsetsListener 可以用于获取窗口插图回调

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

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

boolean keyboardVisible = insets.isVisible(WindowInsets.Type.ime()) 可以确定键盘的可见状态。


0
除了正确的答案之外,当在片段中使用Webview时,我还必须在onCreateView的末尾添加这个。
getActivity().getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_ADJUST_PAN);

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

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

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

0

这可能不适合生产环境,因为它会打开键盘。请注意,类似函数返回的布尔值在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_SHOWN、RESULT_UNCHANGED_HIDDEN、RESULT_SHOWN或RESULT_HIDDEN。如果您得到了RESULT_UNCHANGED_SHOWN,则键盘是可见的。如果您需要在键盘关闭时保持关闭状态,则需要将其关闭。

0

这段代码运行良好

使用此类作为根视图:

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

}

并在活动或片段中设置键盘监听器:

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

    }
});

0

一种不需要LayoutListener的方法

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

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


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接