androidwindowinsetsandroid-window

How to use WindowInsetsCompat correctly to listen to keyboard height change in Activity?


It seems like the official way, to listen to keyboard height change, is to use WindowInsetsCompat based on https://developer.android.com/develop/ui/views/layout/sw-keyboard

( An unofficial is to have an invisible PopupWindow to monitor keyboard height change. But, this is not a reliable method due to today numerous devices with different notch, split screen mode, ... - Is there any way in Android to get the height of virtual keyboard of device )

We try to experiment, to see how we can monitor keyboard height correctly.

Before applying WindowInsetsCompat

enter image description here


After applying WindowInsetsCompat with the following code.

MainActivity.java

public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        View keyboardView = findViewById(R.id.keyboard_view);

        ViewCompat.setOnApplyWindowInsetsListener(getWindow().getDecorView().getRootView(), (v, insets) -> {
            boolean imeVisible = insets.isVisible(WindowInsetsCompat.Type.ime());
            int imeHeight = insets.getInsets(WindowInsetsCompat.Type.ime()).bottom;

            android.util.Log.i("CHEOK", "imeVisible = " + imeVisible + ", imeHeight = " + imeHeight);

            ViewGroup.LayoutParams params = keyboardView.getLayoutParams();
            params.height = imeHeight;
            keyboardView.setLayoutParams(params);

            return insets;
        });
    }
}

activity_main.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    tools:context=".MainActivity">

    <EditText
        android:layout_width="match_parent"
        android:layout_height="0dp"
        android:layout_weight="1"
        android:gravity="top" />

    <LinearLayout
        android:id="@+id/bottom_linear_layout"

        android:layout_width="match_parent"
        android:layout_height="48dp"
        android:orientation="horizontal"
        android:background="#22000000">

        <ImageButton
            android:id="@+id/image_button_0"
            android:layout_width="48dp"
            android:layout_height="48dp"
            android:background="?attr/actionBarItemBackground"
            android:src="@drawable/ic_baseline_alarm_on_24" />
    </LinearLayout>

    <View
        android:id="@+id/keyboard_view"
        android:layout_width="match_parent"
        android:layout_height="0dp"
        android:background="#ff0000" />
</LinearLayout>

Outcome of WindowInsetsCompat (When keyboard is not visible)

enter image description here


Outcome of WindowInsetsCompat (When keyboard is visible)

enter image description here


Here's the observation

  1. For testing purpose, we do not use android:windowSoftInputMode="adjustResize".
  2. After applying WindowInsetsCompat, the top status bar and bottom soft key background become white!
  3. The returned keyboard's height isn't correct. (If the keyboard height is correct, we shouldn't see the red color keyboardView, because we have set the height of keyboardView to be same as keyboard's height)

May I know, when using WindowInsetsCompat to monitor keyboard's height, how can I

  1. Avoid status bar and bottom soft key background from becoming white?
  2. Get the correct keyboard's height?

The following is the workable demo - https://github.com/yccheok/wediary-sandbox/tree/master/keyboard-bottom-sheet-integration

Thanks.


Solution

  • Avoid status bar and bottom soft key background from becoming white?

    This requires to readjust the insets to WindowInsetsCompatby returning ViewCompat.onApplyWindowInsets(v, insets) instead of insets from the setOnApplyWindowInsetsListener() callback.

    Get the correct keyboard's height?

    The insets are calculated based on the far ends of the screen (it makes sense as they're named as window insets, not the activity insets).

    So, in case of the ime/keyboard we are interested in the bottom inset which equals to the top edge of the keyboard to the bottom edge of the screen; this includes the system navigation insets (i.e., the height of the navigation bar).

    The red view is shown because it's drawn on top of the navigation bar; it doesn't take the navBar height into consideration.

    So, to get the exact height of the keyboard, we need to subtract the navBarHeight from the imeHeight:

    int imeHeight = insets.getInsets(WindowInsetsCompat.Type.ime()).bottom 
                    - insets.getInsets(WindowInsetsCompat.Type.systemBars()).bottom;
    

    Please notice the quote of the documentation about ime insets:

    When running on devices with API Level 29 and before, the returned insets are an approximation based on the information available. This is especially true for the IME type, which currently only works when running on devices with SDK level 23 and above.