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


当前回答

也有解决方案与系统嵌入,但它只适用于API >= 21 (Android L)。说你有BottomNavigationView,这是线性布局的孩子,你需要隐藏它时,键盘显示:

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

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

其他回答

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

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返回布尔值。

谢谢,

2012年1月25日新增答案

写了下面的答案后,有人告诉我ViewTreeObserver和其他api的存在,这些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
        }
     }
});

使用一个实用程序,如:

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=" adjuststresize "否则上述解决方案将无法工作。

原来的答案

是的,这是可能的,但它比应该的要难得多。

如果我需要关心键盘何时出现和消失(这是相当经常的),那么我所做的是自定义我的顶级布局类为一个覆盖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);       
    }

    }

然后在活动课上…

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
    }

    ...
}

视图#setOnApplyWindowInsetsListener可以用来获得窗口insets回调

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

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

boolean keyboardVisible = insets.isVisible(WindowInsets.Type.ime())可以给出可见性状态。

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

而不是假设差异编码,我做了这样的事情,因为我在我的应用程序中有菜单选项。

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