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

fhu*_*cho 502 android visibility android-softkeyboard

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

Reu*_*ton 660

NEW ANSWER 于2012年1月25日补充说

自从写下面的答案后,有人告诉我ViewTreeObserver和朋友的存在,这些 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
        }
     }
});
Run Code Online (Sandbox Code Playgroud)

使用如下的实用程序:

public static float dpToPx(Context context, float valueInDp) {
    DisplayMetrics metrics = context.getResources().getDisplayMetrics();
    return TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, valueInDp, metrics);
}
Run Code Online (Sandbox Code Playgroud)

简单!

注意: 您的应用程序必须在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);       
    }

    }
Run Code Online (Sandbox Code Playgroud)

然后在你的Activity类中......

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
    }

    ...
}
Run Code Online (Sandbox Code Playgroud)

  • 在我意识到你必须在你的活动上设置以下属性之前,这对我不起作用:android:windowSoftInputMode ="adjustResize" (62认同)
  • 请注意`heightDiff`将始终包含操作栏的高度.在新的答案中,如果高度大于某个常数,则通过测试忽略,但是100x像素对于xxhdpi设备(如Nexus 4)来说还不够.如果你真的想要使用这个hacky工作,请考虑将该值转换为DPs-周围. (14认同)
  • 似乎在做伎俩.此外,如果你不知道根视图的ID,这里是你如何获得视图:`((ViewGroup)findViewById(android.R.id.content)).getChildAt(0)` (9认同)
  • 如果你使用**实际**根视图(`android.R.id.content`)尝试这个,你将能够更自信地说`System`而不是你的应用程序是改变它的高度的实体.对于Android团队来说,让我们休息并让我们至少知道关于SoftKeyboard输入的基本知识会更安全. (8认同)
  • 注意:不使用WindowManager.LayoutParams.FLAG_FULLSCREEN并使用全屏主题. (7认同)
  • 增加如果(heightDiff> 200)到200 ... 100没有工作,我在QVGA(野火等)设备测试它,也适用.. :) (3认同)
  • 在写这个答案的时候,没有ActionBar这样的东西. (2认同)
  • 请注意,这与软键盘没有任何关系.在Kindle Fire上隐藏和显示等效的系统栏也会导致高度的变化.如果这会导致其他供应商特定情况出现问题,例如三星的多窗口模式,我不会感到惊讶.它还对软键盘的大小做了大量假设,这些假设可以并且确实有所不同(并且不能由应用程序提前确定).因此,这会报告可能会或可能不会由软键盘引起的UI更改. (2认同)
  • @PaulLammertsma即使使用DP,100太小 - 200可能是更安全的选择. (2认同)
  • 使用(高度> 200)键盘打开均匀和(高度<200)键盘关闭事件:) (2认同)
  • `android:windowSoftInputMode="adjustNothing" ` 怎么样?谢谢。我需要将 `android:windowSoftInputMode` 设置为 `adjustNothing`。 (2认同)

Kac*_*chi 298

所以希望这可以帮助某人.

Reuben Scratton给出的新答案非常棒且效率很高,但它确实只有在将windowSoftInputMode设置为adjustResize时才有效.如果将其设置为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.bottom - r.top);
    if (heightDiff > 100) { // if more than 100 pixels, its probably a keyboard...
        ... do something here
    }
 }
}); 
Run Code Online (Sandbox Code Playgroud)

  • `heightDiff> root.getRootView().getHeight()/ 4`是使用高分辨率设备的好价值.100px是空头.在Nexus 5中使用1080x1920 res,1920 - (996-75)>?100 = 999 1920 - (1776-75)>?100 = 219 //键盘在galaxy s2中有480x800 res,800 - (800-38)>?100 = 38 800 - (410-38)>?100 = 428 //键盘上升,幻数100px不够好. (9认同)
  • 这在较新的手机(例如 Pixel 3 XL)上已损坏 (2认同)

Tac*_*0sS 53

它一直是计算机方面的问题,但这个问题仍然令人难以置信!

所以我已经采取了上述答案,并将它们结合起来并进行了一些改进......

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);
        }
    });
}
Run Code Online (Sandbox Code Playgroud)

适合我:)

注意: 如果您发现DefaultKeyboardDP不适合您的设备,请使用该值并发布评论以便每个人知道该值应该是什么...最终我们将获得适合所有设备的正确值!

有关更多详细信息,请查看Cyborg上的实施

  • +1非常感谢你!我正在尝试其他答案,但它们不起作用.然后我找到了你的,它就像一个魅力.真棒代码!:d (2认同)
  • 一个小的更正:private final int EstimatedKeyboardDP = DefaultKeyboardDP +(Build.VERSION.SDK_INT> = Build.VERSION_CODES.LOLLIPOP?48:0); (2认同)
  • 优秀!!无论将“ windowSoftInputMode”设置为“ adjustPan” /“ adjustResize” /“ adjustPan | stateHidden” /“ adjustResize | stateHidden”,即使没有此选项,它也始终有效!在小米8上测试。 (2认同)

Art*_*lin 51

对于迟到的答案很抱歉,但是我创建了一个小助手类来处理打开/关闭事件,通知听众和其他有用的东西,可能有人会发现它有用:

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();
            }
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

用法示例:

final SoftKeyboardStateWatcher softKeyboardStateWatcher 
    = new SoftKeyboardStateWatcher(findViewById(R.id.activity_main_layout);

// Add listener
softKeyboardStateWatcher.addSoftKeyboardStateListener(...);
// then just handle callbacks
Run Code Online (Sandbox Code Playgroud)

  • 这个类很少,但实现肯定是:).谢谢,我会试试这个:) (2认同)

小智 32

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

  1. 高度差的阈值应定义为128 dp,而不是128像素.
    请参阅有关指标和网格的Google设计文档,48 dp是触摸对象的舒适尺寸,按钮最小32 dp.通用软键盘应包括4行按键,因此最小键盘高度应为: 32 dp*4 = 128 dp,这意味着阈值大小应通过乘以设备密度传输到像素.对于xxxhdpi设备(密度4),软键盘高度阈值应为128*4 = 512像素.

  2. 根视图与其可见区域之间的高度差:
    根视图高度 - 状态栏高度 - 可见框架高度=根视图底部 - 可见框架底部,因为状态栏高度等于根视图可见框架的顶部.

    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;
    }
    
    Run Code Online (Sandbox Code Playgroud)

  • 这应该是公认的答案.忽略密度在具有不同形状因子但具有相似像素尺寸的设备上给出了非常不同的结果.谢谢! (4认同)
  • 谢谢......这是一个更好的条件! (2认同)

Pan*_*mar 18

哇,我们有好消息 Android Geeks。是时候告别旧方式了。首先我会添加官方发布说明来阅读和了解更多关于这些方法/类的信息,然后我们将看到这些惊人的方法/类

重要提示:在发布这些类/方法之前,不要将这些添加到您的发布应用程序中

如何检查键盘可见性

val insets = ViewCompat.getRootWindowInsets(view)
val isKeyboardVisible = insets.isVisible(Type.ime())
Run Code Online (Sandbox Code Playgroud)

很少有其他实用程序

如何获得键盘的高度

val insets = ViewCompat.getRootWindowInsets(view)
val keyboardHeight = insets.getInsets(Type.ime()).bottom
Run Code Online (Sandbox Code Playgroud)

如何显示/隐藏键盘

val controller = view.windowInsetsController

// Show the keyboard
controller.show(Type.ime())

// Hide the keyboard
controller.hide(Type.ime())
Run Code Online (Sandbox Code Playgroud)

注意:在 API-30 中添加了WindowInsetsController,因此请等待向后兼容类不可用。

如何监听键盘隐藏/显示事件

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
}
Run Code Online (Sandbox Code Playgroud)

  • 哇,终于!10年后,在如此多的人否认它是必要的之后,哇 (3认同)
  • 我有一个问题,它说:“未解析的引用 isVisible()”,请帮助 (2认同)

小智 8

我花了一点时间来弄明白这一点...我运行了一些CastExceptions,但想通了你可以在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>
Run Code Online (Sandbox Code Playgroud)

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

...如果您不想在每个页面上执行此操作,我建议您使用"Android中的MasterPage".请点击此处链接:http: //jnastase.alner.net/archive/2011/01/08/ldquomaster-pagesrdquo-in-android.aspx

  • @SpencerRuport,这就是为什么它是免费的. (5认同)

Rog*_*ays 7

检查元素的高度是不可靠的,因为像WifiKeyboard这样的键盘高度为零.

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

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


Geo*_*dze 5

我们的想法是,如果您需要隐藏键盘并同时检查软输入状态,请使用以下解决方案:

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

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


归档时间:

查看次数:

263502 次

最近记录:

5 年,10 月 前