Как проверить видимость программной клавиатуры в Android?

Мне нужно сделать очень простую вещь - выяснить, отображается ли программная клавиатура. Возможно ли это в Android?

46 ответов

Существует прямой способ выяснить это. И это не требует каких-либо изменений макета.
Таким образом, он работает и в полноэкранном режиме с погружением.

Хитрость заключается в том, что вы пытаетесь скрыть или показать программную клавиатуру и зафиксировать результат этой попытки.
Никакой паники, это не совсем показывает или скрывает клавиатуру. Мы просто просим о состоянии.

Чтобы оставаться в курсе, вы можете просто повторить операцию, например, каждые 200 миллисекунд, используя обработчик.

Вы найдете реализацию здесь: /questions/40931761/kak-opredelit-vidna-li-programmnaya-klaviatura-na-ustrojstve-android/40931785#40931785

Новый ответ Рубена Скрэттона (вычислите HeightDiff int heightDiff = activityRootView.getRootView().getHeight() - activityRootView.getHeight();) не будет работать в действии, если вы установите режим полупрозрачной строки состояния.

если вы используете полупрозрачную строку состояния, activityRootView.getHeight() никогда не изменит погоду, видна мягкая клавиатура. он всегда будет возвращать высоту активности и статусную строку.

Например, Nexus 4, Android 5.0.1, установить android:windowTranslucentStatus правда, он вернет 1184 навсегда, даже если у меня есть Opend. Если вы установите android:windowTranslucentStatus в false, он вернет Высота правильно, если ime невидим, он вернет 1134(не включая строку состояния). Закройте ime, возможно, вернется 5xx (зависит от высоты ime)

Я не знаю, погода это ошибка, я пробовал на 4.4.4 и 5.0.1, результат тот же.

Таким образом, до сих пор, второй наиболее согласованный ответ, решение Качи будет наиболее безопасным способом расчета высоты. Вот копия:

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

Метод, который не нуждается в LayoutListener

В моем случае я хотел бы сохранить состояние клавиатуры перед заменой моего фрагмента. Я вызываю метод hideSoftInputFromWindow из onSaveInstanceState, который закрывает клавиатуру и возвращает мне, была ли клавиатура видимой или нет.

Этот метод прост, но может изменить состояние вашей клавиатуры.

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

} 

Затем добавьте логический метод, который получает высоту окна в этом экземпляре. Если он не соответствует оригиналу (при условии, что вы не меняете его по пути...), клавиатура открыта.

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!

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()) может дать состояние видимости.

Это, вероятно, не подходит для производства, потому что откроет клавиатуру. Обратите внимание, что логическое значение, возвращаемое аналогичными функциями, не указано в 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, клавиатура была видна. Если вам нужно, чтобы он оставался закрытым, если он был закрыт, вам нужно будет закрыть его.

Этот код прекрасно работает

используйте этот класс для просмотра root:

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) {

    }
});

Может быть, это поможет вам:

InputMethodManager imm = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE);
imm.toggleSoftInput(InputMethodManager.SHOW_FORCED, 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's clientPackage= android указывает, что клавиатура привязана к системе.

Вышеупомянутая информация может быть объединена для строгого способа выяснить, видна ли программная клавиатура.

Я знаю, что это старый пост, но я думаю, что это самый простой из известных мне подходов, и моим тестовым устройством является 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 возвращает логическое значение.

Спасибо,

Ссылаясь на этот ответ @TacB0sS, я разработал один класс в Kotlin. Надеюсь, это будет полезно. Дайте мне знать, если это требует улучшения.

class KeyboardVisibilityObserver(val layRootContainer: View?, val keyboardVisibilityListener: KeyboardVisibilityListener?) {
    var isKeyboardOpen = false
        private set

    private var keyBoardObserver = object : ViewTreeObserver.OnGlobalLayoutListener {

        private val DefaultKeyboardDP = 100

        // Lollipop includes button bar in the root. Add height of button bar (48dp) to maxDiff
        private val EstimatedKeyboardDP = DefaultKeyboardDP + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) 48 else 0

        private val r = Rect()

        override fun onGlobalLayout() {
            if (layRootContainer != null) {
                // Convert the dp to pixels.
                val estimatedKeyboardHeight = TypedValue
                        .applyDimension(TypedValue.COMPLEX_UNIT_DIP, EstimatedKeyboardDP.toFloat(), layRootContainer.resources.displayMetrics).toInt()

                // Conclude whether the keyboard is shown or not.
                layRootContainer.getWindowVisibleDisplayFrame(r)
                val heightDiff = layRootContainer.rootView.height - (r.bottom - r.top)
                val isShown = heightDiff >= estimatedKeyboardHeight

                if (isShown == isKeyboardOpen) {
                    //  Log.d("Keyboard state", "Ignoring global layout change...");
                    return
                }

                isKeyboardOpen = isShown

                keyboardVisibilityListener?.onKeyboardVisibilityChanged(isKeyboardOpen)
            }
        }
    }

    init {
        layRootContainer?.viewTreeObserver?.addOnGlobalLayoutListener(keyBoardObserver)
    }

    // call this in onDestroy
    fun removeObserver(){
        layRootContainer?.viewTreeObserver?.removeOnGlobalLayoutListener(keyBoardObserver)
    }

    interface KeyboardVisibilityListener {
        fun onKeyboardVisibilityChanged(isKeyboardOpen: Boolean)
    }
}

Я знаю, как точно вы можете определить, скрыта клавиатура или нет.

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

Это работает для планшетов. Когда панель навигации отображается горизонтально.

Решение, предложенное 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
                    }
            }
        }
    });
}

В дополнение к правильному ответу мне пришлось добавить это в конец 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);
}
    activity_main.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
        @Override
        public void onGlobalLayout() {

            Rect r = new Rect();
            activity_main.getWindowVisibleDisplayFrame(r);
            int screenHeight =    activity_main.getRootView().getHeight();

            // r.bottom is the position above soft keypad or device button.
            // if keypad is shown, the r.bottom is smaller than that before.
            int keypadHeight = screenHeight - r.bottom;

            Log.d("", "keypadHeight = " + keypadHeight);


            if (keypadHeight > screenHeight * 0.15) { // 0.15 ratio is                      perhaps enough to determine keypad height.
                // keyboard is opened
                AppLog.e("keyboard open", "keyboard open");


            } else {
                AppLog.e("keyboard closed", "keyboard closed");
                // keyboard is closed
            }
        }
    });

InputMethodManager содержит информацию о программной клавиатуре. Вы получаете это от деятельности через:

((InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE))

Вы можете поиграть с этим, чтобы увидеть, что он может вам сказать. Вы можете использовать его, чтобы показать или скрыть программный ввод...

Другие вопросы по тегам