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

Есть ли в Android способ определить, видна ли программная ("мягкая") клавиатура на экране?

41 ответ

Решение

Прямого пути нет - см. Http://groups.google.com/group/android-platform/browse_thread/thread/1728f26f2334c060/5e4910f0d9eb898a, где ответила Дайан Хэкборн из команды Android. Однако вы можете обнаружить это косвенно, проверив, изменился ли размер окна в #onMeasure. См. Как проверить видимость программной клавиатуры в Android?,

Это работает для меня. Может быть, это всегда лучший способ для всех версий.

Было бы эффективно создать свойство видимости клавиатуры и наблюдать, как эти изменения задерживаются, поскольку метод onGlobalLayout вызывается много раз. Также проверьте вращение устройства.

contentView.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
@Override
public void onGlobalLayout() {

    Rect r = new Rect();
    contentView.getWindowVisibleDisplayFrame(r);
    int screenHeight = contentView.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(TAG, "keypadHeight = " + keypadHeight);

    if (keypadHeight > screenHeight * 0.15) { // 0.15 ratio is perhaps enough to determine keypad height.
        // keyboard is opened
    }
    else {
        // keyboard is closed
    }
}
});

Попробуй это:

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

    if (imm.isAcceptingText()) {
        writeToLog("Software Keyboard was shown");
    } else {
        writeToLog("Software Keyboard was not shown");
    }

Я создал простой класс, который можно использовать для этого: https://github.com/ravindu1024/android-keyboardlistener. Просто скопируйте его в свой проект и используйте следующим образом:

KeyboardUtils.addKeyboardToggleListener(this, new KeyboardUtils.SoftKeyboardToggleListener()
{
    @Override
    public void onToggleSoftKeyboard(boolean isVisible)
    {
        Log.d("keyboard", "keyboard visible: "+isVisible);
    }
});

С новой функцией WindowInsetsCompatв версии 1.5.0-alpha02 ядра androidx вы можете легко проверить видимость мягкой клавиатуры, как показано ниже

Цитата из комментария Reddit

val View.keyboardIsVisible: Boolean
    get() = WindowInsetsCompat
        .toWindowInsetsCompat(rootWindowInsets)
        .isVisible(WindowInsetsCompat.Type.ime())

Замечание об обратной совместимости, цитата из примечаний к выпуску

Новые возможности

В WindowInsetsCompat API были обновлены до тех, что используются на платформе Android 11. Это включает в себя новый ime() тип вставки, позволяющий проверить видимость и размер экранной клавиатуры.

Некоторые предостережения относительно ime() типа, он очень надежно работает с API 23+, когда ваша Activity использует adjustResizeоконный режим мягкого ввода. Если вы вместо этого используетеadjustPan режим, он должен надежно работать до API 14.

Ссылки

Очень просто

1. Поместите идентификатор на ваш корневой вид

rootView это просто представление, указывающее на мой корневой вид в этом случае relative layout:

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
                android:layout_width="match_parent"
                android:layout_height="match_parent"
                android:id="@+id/addresses_confirm_root_view"
                android:background="@color/WHITE_CLR">

2. Инициализируйте свое корневое представление в своей деятельности:

RelativeLayout rootView = (RelativeLayout) findViewById(R.id.addresses_confirm_root_view);

3. Определите, открыта или закрыта клавиатура с помощью getViewTreeObserver()

    rootView.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
            @Override
            public void onGlobalLayout() {
                int heightDiff = rootView.getRootView().getHeight() - rootView.getHeight();

                if (heightDiff > 100) { 
                    Log.e("MyActivity", "keyboard opened");
                } else { 
                    Log.e("MyActivity", "keyboard closed");
                }
            }
        });

Так что после долгого времени игры с AccessibilityServices, оконными вставками, определением высоты экрана и т. Д., Я думаю, я нашел способ сделать это.

Отказ от ответственности: он использует скрытый метод в Android, что означает, что он может быть непоследовательным. Тем не менее, в моем тестировании, похоже, работает.

Этот метод является InputMethodManager#getInputMethodWindowVisibleHeight() и существует с Lollipop (5.0).

Вызов, который возвращает высоту в пикселях текущей клавиатуры. Теоретически клавиатура не должна быть 0 пикселей в высоту, поэтому я сделал простую проверку высоты (в Kotlin):

val imm by lazy { context.getSystemService(Context.INPUT_METHOD_SERVICE) as InputMethodManager }
if (imm.inputMethodWindowVisibleHeight > 0) {
    //keyboard is shown
else {
    //keyboard is hidden
}

Я использую Android Hidden API, чтобы избежать рефлексии, когда я вызываю скрытые методы (я делаю это много для приложений, которые я разрабатываю, в основном это хакерские / настраивающие приложения), но это должно быть возможно и с помощью рефлексии:

val imm by lazy { context.getSystemService(Context.INPUT_METHOD_SERVICE) as InputMethodManager }
val windowHeightMethod = InputMethodManager::class.java.getMethod("getInputMethodWindowVisibleHeight")
val height = windowHeightMethod.invoke(imm) as Int
//use the height val in your logic

Вы можете использовать WindowInsetsCompat из androidx.core (версия 1.5.0-rc01) . Этот код будет работать с API 21 и выше. Пример кода Kotlin:

      ViewCompat.setOnApplyWindowInsetsListener(root) { v, insets ->
    val isKeyboardVisible = insets.isVisible(WindowInsetsCompat.Type.ime())
    if (isKeyboardVisible) {
    }
}

root - это корневое представление вашей деятельности.

Я использовал это в качестве основы: http://www.ninthavenue.com.au/how-to-check-if-the-software-keyboard-is-shown-in-android

/**
* To capture the result of IMM hide/show soft keyboard
*/
public class IMMResult extends ResultReceiver {
     public int result = -1;
     public IMMResult() {
         super(null);
}

@Override 
public void onReceiveResult(int r, Bundle data) {
    result = r;
}

// poll result value for up to 500 milliseconds
public int getResult() {
    try {
        int sleep = 0;
        while (result == -1 && sleep < 500) {
            Thread.sleep(100);
            sleep += 100;
        }
    } catch (InterruptedException e) {
        Log.e("IMMResult", e.getMessage());
    }
    return result;
}
}

Затем написал этот метод:

public boolean isSoftKeyboardShown(InputMethodManager imm, View v) {

    IMMResult result = new IMMResult();
    int res;

    imm.showSoftInput(v, 0, result);

    // if keyboard doesn't change, handle the keypress
    res = result.getResult();
    if (res == InputMethodManager.RESULT_UNCHANGED_SHOWN ||
            res == InputMethodManager.RESULT_UNCHANGED_HIDDEN) {

        return true;
    }
    else
        return false;

}

Затем вы можете использовать это для проверки всех полей (EditText, AutoCompleteTextView и т. Д.), Которые могли открыть программную клавиатуру:

    InputMethodManager imm = (InputMethodManager) getActivity().getSystemService(Context.INPUT_METHOD_SERVICE);
    if(isSoftKeyboardShown(imm, editText1) | isSoftKeyboardShown(imm, autocompletetextview1))
        //close the softkeyboard
        imm.toggleSoftInput(InputMethodManager.SHOW_FORCED, 0);

К сожалению, это не идеальное решение, но оно выполняет свою работу.

Наконец-то в 2023 году для этого появилась официальная поддержка!

Вот документация

Чтобы проверить, видна ли клавиатура, сделайте следующее:

      val insets = ViewCompat.getRootWindowInsets(view) ?: return
val imeVisible = insets.isVisible(WindowInsetsCompat.Type.ime())
val imeHeight = insets.getInsets(WindowInsetsCompat.Type.ime()).bottom

Чтобы прослушать изменения видимости клавиатуры, сделайте следующее:

      ViewCompat.setOnApplyWindowInsetsListener(view) { _, insets ->
  val imeVisible = insets.isVisible(WindowInsetsCompat.Type.ime())
  val imeHeight = insets.getInsets(WindowInsetsCompat.Type.ime()).bottom
  insets
}

Примечание. Google рекомендует настроить приложение для отображения от края до края , чтобы оно работало правильно. Они также говорят: «Чтобы добиться наилучшей обратной совместимости с этой реализацией AndroidX, установитеandroid:windowSoftInputMode="adjustResize"к действию в AndroidManifest.xml».

Вы можете использовать результат обратного вызова showSoftInput() и hideSoftInput(), чтобы проверить состояние клавиатуры. Полная информация и пример кода на

http://www.ninthavenue.com.au/how-to-check-if-the-software-keyboard-is-shown-in-android

Наконец-то есть прямой способ начать с Android R на базе Kotlin.

 val imeInsets = view.rootWindowInsets.getInsets(Type.ime()) 
    if (imeInsets.isVisible) { 
     //Ime is visible
     //Lets move our view by the height of the IME
     view.translationX = imeInsets.bottom }

Это должно работать, если вам нужно проверить состояние клавиатуры:

fun Activity.isKeyboardOpened(): Boolean {
    val r = Rect()

    val activityRoot = getActivityRoot()
    val visibleThreshold = dip(UiUtils.KEYBOARD_VISIBLE_THRESHOLD_DP)

    activityRoot.getWindowVisibleDisplayFrame(r)

    val heightDiff = activityRoot.rootView.height - r.height()

    return heightDiff > visibleThreshold;
}

fun Activity.getActivityRoot(): View {
    return (findViewById<ViewGroup>(android.R.id.content)).getChildAt(0);
}

куда UiUtils.KEYBOARD_VISIBLE_THRESHOLD_DP = 100 и dip() - это функция anko, которая преобразует dpToPx:

fun dip(value: Int): Int {
    return (value * Resources.getSystem().displayMetrics.density).toInt()
}

Вы можете обратиться к этому ответу - /questions/34561669/est-li-sposob-uznat-otobrazhaetsya-li-soft-klaviatura/34561690#34561690

Это работало на меня каждый раз.

adb shell dumpsys window InputMethod | grep "mHasSurface"

Он вернет true, если видна программная клавиатура.

Это было намного менее сложным для требований, в которых я нуждался. Надеюсь, что это может помочь:

На основной деятельности:

public void dismissKeyboard(){
    InputMethodManager imm =(InputMethodManager)this.getSystemService(Context.INPUT_METHOD_SERVICE);
    imm.hideSoftInputFromWindow(mSearchBox.getWindowToken(), 0);
    mKeyboardStatus = false;
}

public void showKeyboard(){
    InputMethodManager imm =(InputMethodManager)this.getSystemService(Context.INPUT_METHOD_SERVICE);
    imm.toggleSoftInput(InputMethodManager.SHOW_FORCED, InputMethodManager.HIDE_IMPLICIT_ONLY);
    mKeyboardStatus = true;
}

private boolean isKeyboardActive(){
    return mKeyboardStatus;
}

Основное логическое значение по умолчанию для mKeyboardStatus будет инициализировано как false.

Затем проверьте значение следующим образом и при необходимости выполните действие:

 mSearchBox.requestFocus();
    if(!isKeyboardActive()){
        showKeyboard();
    }else{
        dismissKeyboard();
    }

Я сделал это, установив GlobalLayoutListener, следующим образом:

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

Спасибо за все ответы, я понимаю это для моих собственных обстоятельств

      /**
 * Add global layout listener to observe system keyboard visibility
 */
private void initObserverForSystemKeyboardVisibility() {
    getRootView().getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
        @Override
        public void onGlobalLayout() {
            //Add your own code here
            Log.d("TEST_CODE", "isSystemKeyboardVisible:" + isSystemKeyboardVisible())
        }
    });
}


/**
 * Check system keyboard visibility
 * @return true if visible
 */
public boolean isSystemKeyboardVisible() {
    try {
        final InputMethodManager manager = (InputMethodManager) getContext().getSystemService(Context.INPUT_METHOD_SERVICE);
        final Method windowHeightMethod = InputMethodManager.class.getMethod("getInputMethodWindowVisibleHeight");
        final int height = (int) windowHeightMethod.invoke(manager);
        return height > 0;
    } catch (Exception e) {
        return false;
    }
}
      private fun isKeyboardVisible(rootView: View) =
    ViewCompat.getRootWindowInsets(rootView)!!.isVisible(WindowInsetsCompat.Type.ime())

Как вы, наверное, знаете, Клавиатура Android Software будет видна только при возможном событии набора текста. Другими словами, клавиатура становится видимой только тогда, когда находится EditText. это означает, что вы можете узнать, видна клавиатура или нет, с помощью OnFocusChangeListener.

//Declare this Globally

public boolean isKeyBoardVisible = false;

//In OnCreate *[For Activity]*, OnCreateView *[For Fragment]*

text_send.setOnFocusChangeListener(new View.OnFocusChangeListener() {

    @Override
    public void onFocusChange(View v, boolean hasFocus) {
        if(hasFocus)
            isKeyBoardVisible = true;
        else
            isKeyBoardVisible = false;
    }
});

Теперь вы можете использовать переменную isKeyBoardVisible в любом месте класса, чтобы узнать, открыта ли клавиатура или нет. Это хорошо сработало для меня.

Примечание: этот процесс не работает, когда клавиатура открывается программно с помощью InputMethodManager, потому что это не вызывает OnFocusChangeListener.

Попробуйте этот код, он действительно работает, если KeyboardShown показан, то эта функция возвращает истинное значение....

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

В моем случае у меня был только один EditText управлять в моем макете, чтобы я придумал это решение. Работает хорошо, в основном это обычай EditText который прослушивает фокус и отправляет локальную трансляцию, если фокус меняется или если нажата кнопка назад / сделано. Для работы нужно разместить манекена View в вашем макете с android:focusable="true" а также android:focusableInTouchMode="true" потому что когда вы звоните clearFocus() фокус будет переназначен на первый фокусируемый вид. Пример фиктивного представления:

<View
android:layout_width="1dp"
android:layout_height="1dp"
android:focusable="true"
android:focusableInTouchMode="true"/>

Дополнительная информация

Решение, которое обнаруживает разницу в изменениях компоновки, работает не очень хорошо, потому что оно сильно зависит от плотности экрана, поскольку 100 пикселей может быть много на одном устройстве, а на некоторых других вы не получите ложных срабатываний. Также разные производители имеют разные клавиатуры.

Немного более сжатая версия Котлина на основе ответа @bohdan-oliynyk

private const val KEYBOARD_VISIBLE_THRESHOLD_DP = 100

fun Activity.isKeyboardOpen(): Boolean {
    fun convertDpToPx(value: Int): Int =
        (value * Resources.getSystem().displayMetrics.density).toInt()

    val rootView = findViewById<View>(android.R.id.content)
    val visibleThreshold = Rect()
    rootView.getWindowVisibleDisplayFrame(visibleThreshold)
    val heightDiff = rootView.height - visibleThreshold.height()

    val accessibleValue = convertDpToPx(KEYBOARD_VISIBLE_THRESHOLD_DP)

    return heightDiff > accessibleValue
}

fun Activity.isKeyboardClosed(): Boolean {
    return isKeyboardOpen().not()
}

Попробуй это

      val View.isKeyboardVisible: Boolean
get() = WindowInsetsCompat
    .toWindowInsetsCompat(rootWindowInsets)
    .isVisible(WindowInsetsCompat.Type.ime())

Это функция расширения , написанная на Kotlin для определения видимости клавиатуры . Вызовите его всякий раз, когда захотите узнать состояние видимости клавиатуры.

Использование может получить доступ к этому вот так

      //View can be TextView or any other View that extends by View Class etc.
view.isKeyboardVisible  

Я думаю, что это старый вопрос, но сегодня я наткнулся на хорошую библиотеку, чтобы определить, открыта ли клавиатура ИЛИ нет. Итак, я делюсь здесь.

У меня была похожая проблема, я нашел для этого одну библиотеку и поиграл с ней. Это работает, как и предполагалось. Вот она, Библиотека клавиатуры.

final View activityRootView = findViewById(R.id.rootlayout);
activityRootView.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
        @Override
        public void onGlobalLayout() {

            Rect r = new Rect();
            activityRootView.getWindowVisibleDisplayFrame(r);

            int screenHeight = activityRootView.getRootView().getHeight();
            Log.e("screenHeight", String.valueOf(screenHeight));
            int heightDiff = screenHeight - (r.bottom - r.top);
            Log.e("heightDiff", String.valueOf(heightDiff));
            boolean visible = heightDiff > screenHeight / 3;
            Log.e("visible", String.valueOf(visible));
            if (visible) {
                Toast.makeText(LabRegister.this, "I am here 1", Toast.LENGTH_SHORT).show();
            } else {
                Toast.makeText(LabRegister.this, "I am here 2", Toast.LENGTH_SHORT).show();
            }
        }
});

Вы можете получить его из WindowInsetsCompat, который имеет функцию isVisible. Так:

          val isShown = WindowInsetsCompat
        .toWindowInsetsCompat(binding.root.rootWindowInsets)
        .isVisible(WindowInsetsCompat.Type.ime())

    if (isShown) {
        // keyboard is opened here you can make what do you want. 
        // Also this can be inside a global layout listener
    } 

В Android вы можете обнаружить через оболочку ADB. Я написал и использую этот метод:

{
        JSch jsch = new JSch();
        try {
            Session session = jsch.getSession("<userName>", "<IP>", 22);
            session.setPassword("<Password>");
            Properties config = new Properties();
            config.put("StrictHostKeyChecking", "no");
            session.setConfig(config);
            session.connect();

            ChannelExec channel = (ChannelExec)session.openChannel("exec");
            BufferedReader in = new BufferedReader(new    
            InputStreamReader(channel.getInputStream()));
            channel.setCommand("C:/Android/android-sdk/platform-tools/adb shell dumpsys window 
            InputMethod | findstr \"mHasSurface\"");
            channel.connect();

            String msg = null;
            String msg2 = " mHasSurface=true";

            while ((msg = in.readLine()) != null) {
                Boolean isContain = msg.contains(msg2);
                log.info(isContain);
                if (isContain){
                    log.info("Hiding keyboard...");
                    driver.hideKeyboard();
                }
                else {
                    log.info("No need to hide keyboard.");
                }
            }

            channel.disconnect();
            session.disconnect();

        } catch (JSchException | IOException | InterruptedException e) {
            e.printStackTrace();
        }
    }
}

Ответ @iWantScala отличный, но у меня не работает
rootView.getRootView().getHeight() всегда имеет одинаковое значение

Одним из способов является определение двух переменных

private int maxRootViewHeight = 0;
private int currentRootViewHeight = 0;

добавить глобального слушателя

rootView.getViewTreeObserver()
    .addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
        @Override
        public void onGlobalLayout() {
            currentRootViewHeight = rootView.getHeight();
            if (currentRootViewHeight > maxRootViewHeight) {
                maxRootViewHeight = currentRootViewHeight;
            }
        }
    });

затем проверьте

if (currentRootViewHeight >= maxRootViewHeight) {
    // Keyboard is hidden
} else {
    // Keyboard is shown
}

работает отлично

Он работает с AdjustNothing, используются флаг активности и событий жизненного цикла. Также с Kotlin:

/**
 * This class uses a PopupWindow to calculate the window height when the floating keyboard is opened and closed
 *
 * @param activity The parent activity
 *  The root activity that uses this KeyboardManager
 */
class KeyboardManager(private val activity: AppCompatActivity) : PopupWindow(activity), LifecycleObserver {

    private var observerList = mutableListOf<((keyboardTop: Int) -> Unit)>()

    /** The last value of keyboardTop */
    private var keyboardTop: Int = 0

    /** The view that is used to calculate the keyboard top  */
    private val popupView: View?

    /** The parent view  */
    private var parentView: View

    var isKeyboardShown = false
        private set

    /**
     * Create transparent view which will be stretched over to the full screen
     */
    private fun createFullScreenView(): View {
        val view = LinearLayout(activity)
        view.layoutParams = LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,
                ViewGroup.LayoutParams.MATCH_PARENT)
        view.background = ColorDrawable(Color.TRANSPARENT)
        return view
    }

    init {
        this.popupView = createFullScreenView()
        contentView = popupView

        softInputMode = LayoutParams.SOFT_INPUT_ADJUST_RESIZE or LayoutParams.SOFT_INPUT_STATE_ALWAYS_VISIBLE
        inputMethodMode = INPUT_METHOD_NEEDED

        parentView = activity.findViewById(android.R.id.content)

        width = 0
        height = LayoutParams.MATCH_PARENT

        popupView.viewTreeObserver.addOnGlobalLayoutListener {
            val rect = Rect()
            popupView.getWindowVisibleDisplayFrame(rect)

            val keyboardTop = rect.bottom
            if (this.keyboardTop != keyboardTop) {
                isKeyboardShown = keyboardTop < this.keyboardTop
                this.keyboardTop = keyboardTop
                observerList.forEach { it(keyboardTop) }
            }
        }
        activity.lifecycle.addObserver(this)
    }

    /**
     * This must be called after the onResume of the Activity or inside view.post { } .
     * PopupWindows are not allowed to be registered before the onResume has finished
     * of the Activity
     */
    @OnLifecycleEvent(Lifecycle.Event.ON_RESUME)
    fun start() {
        parentView.post {
            if (!isShowing && parentView.windowToken != null) {
                setBackgroundDrawable(ColorDrawable(0))
                showAtLocation(parentView, Gravity.NO_GRAVITY, 0, 0)
            }
        }
    }

    /**
     * This manager will not be used anymore
     */
    @OnLifecycleEvent(Lifecycle.Event.ON_DESTROY)
    fun close() {
        activity.lifecycle.removeObserver(this)
        observerList.clear()
        dismiss()
    }

    /**
     * Set the keyboard top observer. The observer will be notified when the keyboard top has changed.
     * For example when the keyboard is opened or closed
     *
     * @param observer The observer to be added to this provider
     */
    fun registerKeyboardTopObserver(observer: (keyboardTop: Int) -> Unit) {
        observerList.add(observer)
    }
}

Полезный метод, чтобы всегда видеть над клавиатурой

fun KeyboardManager.updateBottomMarginIfKeyboardShown(
        view: View,
        activity: AppCompatActivity,
        // marginBottom of view when keyboard is hide
        marginBottomHideKeyboard: Int,
        // marginBottom of view when keybouard is shown
        marginBottomShowKeyboard: Int
) {
    registerKeyboardTopObserver { bottomKeyboard ->
        val bottomView = ViewUtils.getFullViewBounds(view).bottom
        val maxHeight = ScreenUtils.getFullScreenSize(activity.windowManager).y
        // Check that view is within the window size
        if (bottomView < maxHeight) {
            if (bottomKeyboard < bottomView) {
                ViewUtils.updateMargin(view, bottomMargin = bottomView - bottomKeyboard +
                        view.marginBottom + marginBottomShowKeyboard)
            } else ViewUtils.updateMargin(view, bottomMargin = marginBottomHideKeyboard)
        }
    }
}

Где getFullViewBounds

fun getLocationOnScreen(view: View): Point {
    val location = IntArray(2)
    view.getLocationOnScreen(location)
    return Point(location[0], location[1])
}

fun getFullViewBounds(view: View): Rect {
     val location = getLocationOnScreen(view)
     return Rect(location.x, location.y, location.x + view.width,
            location.y + view.height)
 }

Где getFullScreenSize

fun getFullScreenSize(wm: WindowManager? = null) =
            getScreenSize(wm) { getRealSize(it) }

private fun getScreenSize(wm: WindowManager? = null, block: Display.(Point) -> Unit): Point {
    val windowManager = wm ?: App.INSTANCE.getSystemService(Context.WINDOW_SERVICE)
            as WindowManager
    val point = Point()
    windowManager.defaultDisplay.block(point)
    return point
}

Где updateMargin

fun updateMargin(
        view: View,
        leftMargin: Int? = null,
        topMargin: Int? = null,
        rightMargin: Int? = null,
        bottomMargin: Int? = null
) {
    val layoutParams = view.layoutParams as ViewGroup.MarginLayoutParams
    if (leftMargin != null) layoutParams.leftMargin = leftMargin
    if (topMargin != null) layoutParams.topMargin = topMargin
    if (rightMargin != null) layoutParams.rightMargin = rightMargin
    if (bottomMargin != null) layoutParams.bottomMargin = bottomMargin
    view.layoutParams = layoutParams
}

Я преобразовал ответ в kotlin, надеюсь, это поможет пользователям kotlin.

private fun checkKeyboardVisibility() {
    var isKeyboardShowing = false

    binding.coordinator.viewTreeObserver.addOnGlobalLayoutListener {
        val r = Rect()
        binding.coordinator.getWindowVisibleDisplayFrame(r)
        val screenHeight = binding.coordinator.rootView.height

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


        if (keypadHeight > screenHeight * 0.15) { // 0.15 ratio is perhaps enough to determine keypad height.
            // keyboard is opened
            if (!isKeyboardShowing) {
                isKeyboardShowing = true

            }
        } else {
            // keyboard is closed
            if (isKeyboardShowing) {
                isKeyboardShowing = false

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