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


516

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


9
尽管Reuben Scratton的回答很好,但似乎在平板电脑上还是坏了。我将diff> 128替换为diff> screenHeight / 3。
金斯敦

2
Reuben Scratton的回答很好,但是我需要KaChi进行调整才能真正使用它。
卡兰

1
为什么Google不能使标准的内置方法适用于所有键盘应用程序?
水果

4
它仍然是我的兄弟,这不是系统功能……
longi

1
十年后仍然缺少该API绝对是胡说八道。很高兴我离开了Android。
Reuben Scratton

Answers:


674

NEW ANSWER 添加于2012年1月25日

自从写下以下答案以来,有人让我了解到ViewTreeObserver和朋友,自版本1以来一直潜伏在SDK中的API。

不需要自定义Layout类型,一种更简单的解决方案是为活动的根视图提供一个已知的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
        }
     }
});

使用实用程序,例如:

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

简单!

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

    }

然后在您的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
    }

    ...
}

62
直到我意识到您必须在活动中设置以下属性后,此方法才对我有用:android:windowSoftInputMode =“ adjustResize”
ajh158 2011年

9
似乎正在解决问题。另外,如果您不知道根视图的ID,可以通过以下方法获取视图:((ViewGroup) findViewById(android.R.id.content)).getChildAt(0)
Goldsmith

8
如果您使用实际的根视图(android.R.id.content)尝试此操作,您将可以更有把握地说,System而不是您的应用程序是更改其高度的实体。让Android团队休息一下,让我们至少知道有关SoftKeyboard输入的基本知识,这样会更加安全。
Graeme

14
请注意,heightDiff它将始终包括操作栏的高度。在通过测试该高度是否大于某个常数而被忽略的新答案中,但对于Nexus 4之类的xxhdpi设备,100像素不足以解决这个问题,如果您确实想使用此棘手的工作,请考虑将该值转换为DP,周围。
Paul Lammertsma

8
注意:不适用于WindowManager.LayoutParams.FLAG_FULLSCREEN和全屏主题。
VAV 2014年

303

因此希望这可以帮助某人。

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 > 0.25*activityRootView.getRootView().getHeight()) { // if more than 25% of the screen, its probably a keyboard...
        ... do something here
    }
 }
}); 

1
这是为我工作的那个。我试图从TwoDScrollerView类似于stackoverflow.com/a/5224088/530513的自定义中检测键盘状态,尽管也具有缩放功能。这个孩子不是一个简单的人,ImageView而是一个自定义布局(扩展RelativeLayout),但是尽管进行了设置,但仍无法使用推荐的解决方案来检测键盘android:windowSoftInputMode="adjustResize"。谢谢!
David O'Meara

1
谢谢你,谢谢你,谢谢你!AdjustResize不适用于我的应用程序,并且您的解决方案运行完美。
dwemthy 2012年

1
ActionBar和一起使用ActionBarSherlock。非常感谢!顺便说一句,有一种方法r.height():)
德米特里·扎伊采夫

1
我会在23小时内在此处悬赏,以某种方式标记此答案。
德米特里·扎耶采夫

9
heightDiff > root.getRootView().getHeight() / 4与高分辨率设备一起使用具有很高的价值。100px短。在Nexus 5中,分辨率为1080x1920,1920-(996-75)>?100 = 999 1920-(1776-75)>?100 = 219 // //键盘在480x800分辨率的galaxy s2中为800-(800-38)>?100 = 38800-(410-38)>?100 = 428 //键盘向上,魔术数字100px不够好。
Flask_KR 2015年

55

就计算机而言,它一直存在,但是这个问题仍然令人难以置信!

因此,我采取了以上答案,并对其进行了组合和完善...

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


2
+1非常感谢!我正在尝试其他答案,但是它们不起作用。然后我找到了你的,它就像一个魅力。很棒的代码!:D
Kevin van Mierlo 2014年

这仅在您添加时才有效:android:windowSoftInputMode =“ stateHidden | adjustPan”或android:windowSoftInputMode =“ stateHidden | adjustResize”谢谢!!!
莉娜·布鲁

你确定吗?如果是内存服务器,则当android:windowSoftInputMode具有默认值时,我也可以正确接收事件...唯一无法解决的是屏幕的行为,因此我手动缩小了屏幕...
TacB0sS

2
一个小小的修正:private final int EstimatedKeyboardDP = DefaultKeyboardDP +(Build.VERSION.SDK_INT> = Build.VERSION_CODES.LOLLIPOP?48:0);
binaryKarmic 2014年

2
优秀的!!无论将“ windowSoftInputMode”设置为“ adjustPan” /“ adjustResize” /“ adjustPan | stateHidden” /“ adjustResize | stateHidden”,即使没有此选项,它也始终有效!测试在小蜜8
周红波

52

抱歉,我的回答很晚,但是我创建了一个小助手类来处理打开/关闭事件,并通知监听者和其他有用的东西,也许有人会觉得有用:

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

用法示例:

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

// Add listener
softKeyboardStateWatcher.addSoftKeyboardStateListener(...);
// then just handle callbacks

2
该类不是很多,但是实现肯定是:)。谢谢,我会尝试:)
Atul O Holic 2014年

1
如果有问题,请联系我:)我在2个项目中成功使用了它
Artem Zinnatullin 2014年

在尝试了上面的许多示例并遇到了一些小问题之后,该示例对我来说在许多不同的设备(包括xxhdpi)上效果最佳。另外,它在自己的可重用类中。我将其转换为可用于单机器人。
kheit 2014年

有些键盘有时会在顶部多排自定义键(例如,预测的单词)。这些显然不是键盘本身的一部分,因为使用时getLastKeyboardHeightInPx()不包括该行的高度。您是否也知道将其考虑在内的方法?
ygesher 2014年

仅当您准备妥当出现键盘时更改布局高度更改时,此方法才有效。对?
M. Usman Khan 2014年

33

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

  1. 高度差的阈值应定义为128 dp,而不是128像素
    参阅谷歌设计文档的指标和电网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;
    }

4
这应该是公认的答案。忽略密度会给我带来不同的结果,这些结果在具有不同形状因数但像素大小相似的设备上产生。谢谢!
姜麦克默里2014年

2
谢谢...这是一个更好的条件!
TacB0sS 2014年

1
优秀的。isKeyboardShown()方法就是我们所需要的。谢谢
Danylo Volokh 2015年

它也在2020年工作。完美的答案。我尝试了所有代码,但是在此上效果很好。
米特什·Ja那

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>

这样,您就不会遇到任何强制转换问题。

...,如果您不想在每个页面上都这样做,建议您使用“ Android中的MasterPage”。请参阅此处的链接:http : //jnastase.alner.net/archive/2011/01/08/ldquomaster-pagesrdquo-in-android.aspx


如果您没有相同的包/类名称,请小心将其粘贴到XML中。Eclipse只是决定冻结,您必须将其关闭。如此专业的产品。/ s
Spencer Ruport

5
@SpencerRuport,这就是为什么它是免费的。
科迪2012年

@DoctorOreo-获取IntelliJ。它是免费的,不会烂。
标记

@Mark-发布此消息几个月后,我确实尝试了IntelliJ。IMO,比Eclipse好得多。我认为他们的所有产品(大部分)都很棒。我什至买了一些。
科迪

很抱歉恢复了这样的旧注释线程。我很高兴您正在使用它并享受它。我喜欢使用IntelliJ以及iOS的AppCode和Python的PyCharm。干杯!
2013年


5

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

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

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


这是不使用高度和全部的作品...谢谢...你节省了我的时间...
Vji 2015年

4

我发现@Reuben_Scratton方法和@Yogesh方法的组合似乎效果最好。结合他们的方法将产生如下结果:

final View activityRootView = findViewById(R.id.activityRoot);
activityRootView.getViewTreeObserver().addOnGlobalLayoutListener(new OnGlobalLayoutListener() {
  @Override
  public void onGlobalLayout() {
    if (getResources().getConfiguration().keyboardHidden == Configuration.KEYBOARDHIDDEN_NO) { // Check if keyboard is not hidden
       // ... do something here
    }
  }
});

始终为Configuration.KEYBOARDHIDDEN_NO。
fantouch 2014年

4

您可以使用活动的decorView观察软键盘的隐藏。

public final class SoftKeyboardUtil {
    public static final String TAG = "SoftKeyboardUtil";
    public static void observeSoftKeyBoard(Activity activity , final OnSoftKeyBoardHideListener listener){
        final View decorView = activity.getWindow().getDecorView();
        decorView.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
            @Override
            public void onGlobalLayout() {
                Rect rect = new Rect();
                decorView.getWindowVisibleDisplayFrame(rect);
                int displayHight = rect.bottom - rect.top;
                int hight = decorView.getHeight();
                boolean hide = (double)displayHight / hight > 0.8 ;
                if(Log.isLoggable(TAG, Log.DEBUG)){
                    Log.d(TAG ,"DecorView display hight = "+displayHight);
                    Log.d(TAG ,"DecorView hight = "+ hight);
                    Log.d(TAG, "softkeyboard visible = " + !hide);
                }

                listener.onSoftKeyBoardVisible(!hide);

            }
        });
    }



    public interface OnSoftKeyBoardHideListener{
        void onSoftKeyBoardVisible(boolean visible);
    }
}

4

我没有做差异编码,而是做了类似的事情,因为我在应用程序中有菜单选项。

final View root= findViewById(R.id.myrootview); 
root.getViewTreeObserver().addOnGlobalLayoutListener(new OnGlobalLayoutListener() {
@Override
    public void onGlobalLayout() {
        int heightDiff = root.getRootView().getHeight() - root.getHeight();

        Rect rectgle= new Rect();
        Window window= getWindow();
        window.getDecorView().getWindowVisibleDisplayFrame(rectgle);
        int contentViewTop=                     
          window.findViewById(Window.ID_ANDROID_CONTENT).getTop();
        if(heightDiff <= contentViewTop){
            //Soft KeyBoard Hidden
        }else{
            //Soft KeyBoard Shown
        }
     }
});

对于android:windowSoftInputMode =“ adjustPan”无效。我希望在出现软键盘后,屏幕不会缩水。您能告诉我们任何解决方法,以便它甚至适用于AdjustPan
Shirish Herwade

4

还有系统插入的解决方案,但仅适用于API >= 21Android L)。假设您拥有BottomNavigationView,它是的子级,LinearLayout在显示键盘时您需要将其隐藏:

> LinearLayout
  > ContentView
  > BottomNavigationView

您需要做的就是以LinearLayout这种方式扩展:

public class KeyboardAwareLinearLayout extends LinearLayout {
    public KeyboardAwareLinearLayout(Context context) {
        super(context);
    }

    public KeyboardAwareLinearLayout(Context context, @Nullable AttributeSet attrs) {
        super(context, attrs);
    }

    public KeyboardAwareLinearLayout(Context context,
                                     @Nullable AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
    }

    public KeyboardAwareLinearLayout(Context context, AttributeSet attrs,
                                     int defStyleAttr, int defStyleRes) {
        super(context, attrs, defStyleAttr, defStyleRes);
    }

    @Override
    public WindowInsets onApplyWindowInsets(WindowInsets insets) {
        int childCount = getChildCount();
        for (int index = 0; index < childCount; index++) {
            View view = getChildAt(index);
            if (view instanceof BottomNavigationView) {
                int bottom = insets.getSystemWindowInsetBottom();
                if (bottom >= ViewUtils.dpToPx(200)) {
                    // keyboard is shown
                    view.setVisibility(GONE);
                } else {
                    // keyboard is hidden
                    view.setVisibility(VISIBLE);
                }
            }
        }
        return insets;
    }
}

这个想法是,当显示键盘时,系统插图会被更改为相当大的.bottom值。


4

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

如果有人需要它(它也可以在Xamarin中使用),则该方法的名称完全相同,并且需要通过InputMethodManager上的Class属性以相同的方式进行访问。
Konstantin Severy,

请谨慎使用,它是不受支持的API(由于某种原因它被隐藏了),对于初学者来说,它在KitKat上不起作用。
Daniele Ricci

3

这些解决方案都无法按原样适用于棒棒糖。在Lollipop中activityRootView.getRootView().getHeight()包括按钮栏的高度,而测量视图时不包括。我采用了上面最好/最简单的解决方案来与Lollipop配合使用。

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

为什么stackoverflow.com/a/18992807/2914140上的类似解决方案对您没有帮助,您的解决方案有何不同?
CoolMind

3

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

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

因此,随着所有新的高分辨率手机的发布,我认为长期使用硬编码值并不是一个好主意。

经过在各种屏幕和设备上进行测试后,我发现一种更好的方法是使用百分比。得到decorView和您的应用程序内容之间的差异,然后检查该差异的百分比。根据我得到的统计数据,大多数导航栏(与大小,分辨率等无关)将占屏幕的3%至5%。好像键盘已打开一样,它占据了屏幕的47%至55%。

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


3

我使用了Reuban答案的一个略微变体,在某些情况下,尤其是使用高分辨率设备,事实证明这对您更有帮助。

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

这是什么R.id.activityRoot
ranjith's

2
无需创建和使用R.id.activityRoot,只需使用android.R.id.content正是您所需要的。
Marcin Orlowski '16

3

就计算机而言,它一直存在,但是这个问题仍然令人难以置信!因此,我采取了以上答案,并对其进行了组合和完善...

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 Rect r = new Rect();

        @Override
        public void onGlobalLayout() {
            activityRootView.getWindowVisibleDisplayFrame(r);

            int heightDiff = activityRootView.getRootView().getHeight() - (r.bottom - r.top);
            boolean isOpen = heightDiff > 100;
            if (isOpen == wasOpened) {
                logDebug("Ignoring global layout change...");
                return;
            }

            wasOpened = isOpen;
            listener.onVisibilityChanged(isOpen);
        }
    });
}

这个对我有用。


3

尝试这个:

final View activityRootView = getWindow().getDecorView().getRootView();
activityRootView.getViewTreeObserver().addOnGlobalLayoutListener(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);

        int heightDiff = activityRootView.getRootView().getHeight() - (r.bottom - r.top);
        if (heightDiff < activityRootView.getRootView().getHeight() / 4 ) { // if more than 100 pixels, its probably a keyboard...
             // ... do something here ... \\
        }
    }
});

2

我的答案与Kachi的答案基本相同,但是我将其包装到一个不错的帮助程序类中,以整理其在整个应用程序中的使用方式。

import android.app.Activity;
import android.app.Fragment;
import android.graphics.Rect;
import android.view.View;
import android.view.ViewTreeObserver.OnGlobalLayoutListener;

/**
 * Detects Keyboard Status changes and fires events only once for each change
 */
public class KeyboardStatusDetector {
    KeyboardVisibilityListener visibilityListener;

    boolean keyboardVisible = false;

    public void registerFragment(Fragment f) {
        registerView(f.getView());
    }

    public void registerActivity(Activity a) {
        registerView(a.getWindow().getDecorView().findViewById(android.R.id.content));
    }

    public KeyboardStatusDetector registerView(final View v) {
        v.getViewTreeObserver().addOnGlobalLayoutListener(new OnGlobalLayoutListener() {
            @Override
            public void onGlobalLayout() {
                Rect r = new Rect();
                v.getWindowVisibleDisplayFrame(r);

                int heightDiff = v.getRootView().getHeight() - (r.bottom - r.top);
                if (heightDiff > 100) { // if more than 100 pixels, its probably a keyboard...
                    /** Check this variable to debounce layout events */
                    if(!keyboardVisible) {
                        keyboardVisible = true;
                        if(visibilityListener != null) visibilityListener.onVisibilityChanged(true);
                    }
                } else {
                    if(keyboardVisible) {
                        keyboardVisible = false;
                        if(visibilityListener != null) visibilityListener.onVisibilityChanged(false);
                    }
                }
            }
        });

        return this;
    }

    public KeyboardStatusDetector setVisibilityListener(KeyboardVisibilityListener listener) {
        visibilityListener = listener;
        return this;
    }

    public static interface KeyboardVisibilityListener {
        public void onVisibilityChanged(boolean keyboardVisible);
    }
}

您可以使用它来检测整个应用中任何地方的键盘变化,如下所示:

    new KeyboardStatusDetector()
            .registerFragment(fragment)  //register to a fragment 
            .registerActivity(activity)  //or register to an activity
            .registerView(view)          //or register to a view
            .setVisibilityListener(new KeyboardVisibilityListener() {
                @Override
                public void onVisibilityChanged(boolean keyboardVisible) {
                    if(keyboardVisible) {
                       //Do stuff for keyboard visible
                    }else {
                       //Do stuff for keyboard hidden
                    }
                }
            });

注意:仅使用“注册”调用之一。它们都是一样的,只是为了方便


2

您可以尝试一下,对我非常有用:

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

if (imm.isAcceptingText()) {
    //Software Keyboard was shown..
} else {
    //Software Keyboard was not shown..
}

1
不要使用它,如果使用后退键隐藏了键盘,但是视图具有焦点,则至少返回了棉花糖
Maragues'Jul

2
总是可见的响应
jose920405 '16

2

更改viewpager中片段的方向时,我很难保持键盘状态。我不知道为什么,但是它似乎有点奇怪,并且行为与标准Activity不同。

在这种情况下,要保持键盘状态,首先应添加android:windowSoftInputMode = "stateUnchanged"AndroidManifest.xml。但是,您可能会注意到,这实际上并不能解决整个问题-如果以前在方向更改之前打开过键盘,则不会为我打开键盘。在所有其他情况下,该行为似乎都是正确的。

然后,我们需要实现此处提到的解决方案之一。我找到的最干净的是George Maisuradze的-使用hideSoftInputFromWindow的布尔回调:

InputMethodManager imm = (InputMethodManager) getSystemService(Activity.INPUT_METHOD_SERVICE);
return imm.hideSoftInputFromWindow(mViewPager.getWindowToken(), 0);

我将此值存储在Fragment的onSaveInstanceState方法中并进行了检索onCreate。然后,我强行将键盘onCreateView的值显示为in true(如果键盘在Fragment销毁之前实际隐藏之前可见,则返回true)。


1

这是我的解决方案,它可以工作。无需查找像素大小,只需检查内容视图的高度是否已更改:

// Scroll to the latest comment whenever the keyboard is shown
commentsContent.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {

        private int oldHeight;

        @Override
        public void onGlobalLayout() {
            int newHeight = commentsContent.getMeasuredHeight();
            if (newHeight < oldHeight) {
                // Check for the keyboard showing in case the height difference
                // is a result of orientation change
                if (isSoftKeyboardShowing(CommentsActivity.this)) {
                    // Keyboard is showing so scroll to the latest comment
                    scrollToLatestComment();
                }
            }
            oldHeight = newHeight;
        }

    });


public static boolean isSoftKeyboardShowing(Activity activity) {
    InputMethodManager inputMethodManager = (InputMethodManager) activity.getSystemService(Activity.INPUT_METHOD_SERVICE);
    return inputMethodManager.isActive();
}

无论键盘是否打开,inputMethodManager.isActive()始终对我返回true
EionRobb 2014年

1

不要编写任何硬代码。最好的方法是您必须在使用KeyBord Show聚焦于EditText时调整视图大小。您可以使用以下代码将活动的调整大小属性添加到清单文件中。

android:windowSoftInputMode="adjustResize"


1

有一个直接的方法可以找到答案。并且,它不需要任何布局更改。
因此,它也可以在沉浸式全屏模式下工作。

诀窍是您尝试隐藏或显示软键盘并捕获该尝试的结果。
不必担心,这实际上不会显示或隐藏键盘。我们只是要求状态。

为了保持最新状态,您可以使用Handler简单地重复该操作,例如每200毫秒重复一次。

您可以在此处找到实现:https : //stackoverflow.com/a/27567074/2525452


1

我认为此方法将帮助您找出是否可见键盘。

 public Boolean isSoftKeyBoardVisible(){
    InputMethodManager imm = (InputMethodManager)getSystemService(Context.INPUT_METHOD_SERVICE);

    if (imm.isAcceptingText()) {
        Log.d(TAG,"Software Keyboard was shown");
        return true;
    } else {
        Log.d(TAG,"Software Keyboard was not shown");
        return false;
    }

}

总是可见的响应
jose920405 '16

0

Reuben Scratton的新答案(计算HeightDiff int heightDiff = activityRootView.getRootView().getHeight() - activityRootView.getHeight();如果您设置了半透明状态栏模式,那么)将无法使用。

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

例如,将Nexus 4,Android 5.0.1设置android:windowTranslucentStatus为true,即使ime已打开,它将永远返回1184。如果您设定android:windowTranslucentStatus为false,它将正确返回Height,如果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

该方法不需要LayoutListener

就我而言,我想在替换Fragment之前保存键盘的状态。我所说的方法 hideSoftInputFromWindowonSaveInstanceState,该关闭键盘并返回键盘我是否可见或不可见。

此方法很简单,但可能会更改键盘的状态。


0

我知道这是一篇过时的文章,但是我认为这是我所知道的最简单的方法,而我的测试设备是Nexus5。我没有在其他设备上尝试过。希望其他人如果发现我的代码不好也可以分享他们的方法:)

public static boolean isKeyboardShown(Context context, View view) {
        if (context == null || view == null) {
            return false;
        }
        InputMethodManager imm = (InputMethodManager) context
                .getSystemService(Context.INPUT_METHOD_SERVICE);
        return imm.hideSoftInputFromWindow(view.getWindowToken(), 0); 
}

imm.hideSoftInputFromWindow返回布尔值。

谢谢,


0
if (keyopen())
{
                InputMethodManager imm = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE);
                imm.toggleSoftInput(InputMethodManager.HIDE_IMPLICIT_ONLY,0);            
}

上面的功能是我用来检查键盘是否可见的功能。如果是,那么我将其关闭。

下面显示了所需的两种方法。

首先,在onCreate中定义可行的窗口高度。

protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

//  add to onCreate method
    Rect rectgle= new Rect();
    Window window= getWindow();
    window.getDecorView().getWindowVisibleDisplayFrame(rectgle);
    sheight= rectgle.bottom;
//

} 

然后,添加一个布尔方法,该方法在该实例处获取Window的高度。如果它与原件不符(假设您在途中未进行更改...),则说明键盘已打开。

public boolean keyopen()
{
    Rect rectgle= new Rect();
    Window window= getWindow();
    window.getDecorView().getWindowVisibleDisplayFrame(rectgle);
    int curheight= rectgle.bottom;

    if (curheight!=sheight)
    {
        return true;
    }
    else
    {
        return false;
    }
}

Frotz!


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

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

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.