1

I have an activity that is using "adjustPan" as its resize config, and I need to calculate keyboard height without using "adjustResize" because I need to keep some views as fullscreen (which means they should stay where they are, the keyboard should hide them), and position a view right above the keyboard. Our application has a message button and I open the keyboard via the button click. When it happens, I use an OnGlobalLayoutListener and use "getWindowVisibleDisplayFrame" method to get the keyboard's height. Here is some code for it:

private void message()
    {
        InputMethodManager methodManager =
                (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE);
        if (methodManager != null && !isKeyboardOpen)
        {
            methodManager.toggleSoftInput(InputMethodManager.SHOW_FORCED, 0);
            if (bottomCoordinate == 0)
            {
                RelativeLayout layout = findViewById(getFullScreenContainerId());
                layout.getViewTreeObserver().addOnGlobalLayoutListener(
                        new ViewTreeObserver.OnGlobalLayoutListener()
                        {
                            @Override
                            public void onGlobalLayout()
                            {
                                layout.getViewTreeObserver().removeOnGlobalLayoutListener(this);
                                Rect r = new Rect();
                                layout.getWindowVisibleDisplayFrame(r);

                                bottomCoordinate = r.bottom - r.top;

                                translateMessageView(bottomCoordinate);
                            }
                        });
            }
            else
                translateMessageView(bottomCoordinate);
            isKeyboardOpen = true;
        }
    }

"translateMessageView" basically sets the view's Y coordinate to "bottomCoordinate - view.getHeight()". This works good up until the autocorrect part of the soft keyboard applications becomes visible. Apparently "getWindowVisibleDisplayFrame" method doesn't seem to add autocorrect part of the view or "onGlobalLayout" method is not called when soft keyboard's autocorrect part shows up, and the positioned view stays under it which makes it half-visible. I need to be able to adjust its position again, so what should I do? What is the correct approach for this? Any suggestion is valuable, thank you.

Furkan Yurdakul
  • 2,801
  • 1
  • 15
  • 37
  • Have you found a solution? – Pierre Dec 02 '19 at 06:28
  • @Pierre Umm, yeah. After detecting a keystroke with `TextWatcher` on the `EditText`, I got the visible frame again on a `Handler.post()` call which seems to be working so far. But I found that out 6 months later or so, that's why I forgot to answer my own question. – Furkan Yurdakul Dec 02 '19 at 06:31
  • ah, so when the text changes and the extra bit pops up it doesn't change your hidden views height so that part covers up your input? – Pierre Dec 02 '19 at 06:33
  • 1
    @Pierre Not exactly, `onGlobalLayout` is not called but the values of `getWindowVisibleDisplayFrame(r)` changes so I move the view according to the result after a text change. – Furkan Yurdakul Dec 02 '19 at 06:38

2 Answers2

5

Here is the way I detect the Keyboard height in any activity, this also caters for the notch/cutout height if there is one.

KeyboardHeightProvider.java

import android.content.Context;
import android.graphics.Rect;
import android.graphics.drawable.ColorDrawable;
import android.util.DisplayMetrics;
import android.view.DisplayCutout;
import android.view.Gravity;
import android.view.View;
import android.view.ViewGroup;
import android.view.ViewTreeObserver;
import android.view.WindowInsets;
import android.view.WindowManager;
import android.widget.LinearLayout;
import android.widget.PopupWindow;

import androidx.core.view.DisplayCutoutCompat;
import androidx.core.view.OnApplyWindowInsetsListener;
import androidx.core.view.ViewCompat;
import androidx.core.view.WindowInsetsCompat;

public class KeyboardHeightProvider extends PopupWindow implements OnApplyWindowInsetsListener {
    private View decorView;

    private DisplayMetrics metrics;

    private LinearLayout popupView;
    private ViewTreeObserver.OnGlobalLayoutListener globalLayoutListener;

    private Rect insets = new Rect(0, 0, 0, 0);

    public KeyboardHeightProvider(Context context, WindowManager windowManager, View decorView, KeyboardHeightListener listener) {
        super(context);
        this.decorView = decorView;

        metrics = new DisplayMetrics();
        windowManager.getDefaultDisplay().getMetrics(metrics);

        popupView = new LinearLayout(context);
        popupView.setLayoutParams(new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT));
        globalLayoutListener = () -> {
            windowManager.getDefaultDisplay().getMetrics(metrics);

            int keyboardHeight = getKeyboardHeight();
            
            boolean screenLandscape = metrics.widthPixels > metrics.heightPixels;
            if (listener != null) {
                listener.onKeyboardHeightChanged(keyboardHeight, screenLandscape);
            }
        };

        setContentView(popupView);

        setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE | WindowManager.LayoutParams.SOFT_INPUT_STATE_ALWAYS_VISIBLE);
        setInputMethodMode(PopupWindow.INPUT_METHOD_NEEDED);
        setWidth(0);
        setHeight(ViewGroup.LayoutParams.MATCH_PARENT);
        setBackgroundDrawable(new ColorDrawable(0));

        ViewCompat.setOnApplyWindowInsetsListener(popupView, this);
    }

    public void start() {
        popupView.getViewTreeObserver().addOnGlobalLayoutListener(globalLayoutListener);
        decorView.post(() -> showAtLocation(decorView, Gravity.NO_GRAVITY, 0, 0));
    }

    @Override
    public void dismiss() {
        popupView.getViewTreeObserver().removeOnGlobalLayoutListener(globalLayoutListener);
        super.dismiss();
    }

    @Override
    public WindowInsetsCompat onApplyWindowInsets(View v, WindowInsetsCompat insets) {
        DisplayCutoutCompat cutoutCompat = insets.getDisplayCutout();
        if (cutoutCompat != null) {
            this.insets.set(cutoutCompat.getSafeInsetLeft(), cutoutCompat.getSafeInsetTop(), cutoutCompat.getSafeInsetRight(), cutoutCompat.getSafeInsetBottom());
        } else {
            this.insets.set(insets.getSystemWindowInsetLeft(), insets.getSystemWindowInsetTop(), insets.getSystemWindowInsetRight(), insets.getSystemWindowInsetBottom());
        }

        if (decorView != null && Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
            WindowInsets rootWindowInsets = decorView.getRootWindowInsets();
            if (rootWindowInsets != null) {
                DisplayCutout displayCutout = rootWindowInsets.getDisplayCutout();
                if (displayCutout != null) {
                    this.insets.set(displayCutout.getSafeInsetLeft(), displayCutout.getSafeInsetTop(), displayCutout.getSafeInsetRight(), displayCutout.getSafeInsetBottom());
                }
            }
        }

        return insets;
    }

    public int getKeyboardHeight() {
        Rect rect = new Rect();
        popupView.getWindowVisibleDisplayFrame(rect);

        int keyboardHeight = metrics.heightPixels - (rect.bottom - rect.top) - (insets.bottom - insets.top);
        int resourceID = context.getResources().getIdentifier("status_bar_height", "dimen", "android");
        if (resourceID > 0) {
            keyboardHeight -= context.getResources().getDimensionPixelSize(resourceID);
        }
        if (keyboardHeight < 100) {
            keyboardHeight = 0;
        }
        
        return keyboardHeight;
    }

    public interface KeyboardHeightListener {
        void onKeyboardHeightChanged(int height, boolean isLandscape);
    }
}

On your activity:

  1. android:windowSoftInputMode can be anything, mine is stateHidden|adjustNothing

  2. implement KeyboardHeightProvider.KeyboardHeightListener

  3. add a global variable:

     private KeyboardHeightProvider keyboardHeightProvider;
    
  4. in onCreate add the line:

     keyboardHeightProvider = new KeyboardHeightProvider(this, getWindowManager(), getWindow().getDecorView(), this);
    
  5. in onResume add the line:

     keyboardHeightProvider.start();
    
  6. in onPause add the line:

     keyboardHeightProvider.dismiss();
    
  7. in onKeyboardHeightChanged

     @Override
     public void onKeyboardHeightChanged(int height, boolean isLandscape) {
         //This will be called anytime the keyboard height has changed.
    
         boolean keyboardOpen = height > 0;
    
         //do what you want with Keyboard Height
     }
    
Pierre
  • 8,397
  • 4
  • 64
  • 80
  • i just tried then "onKeyboardHeightChanged" called when keyboard shown, i want to get keyboard height at run time – famfamfam Apr 14 '21 at 09:04
  • @famfamfam You can add a keyboard Height getter method. Move `DisplayMetrics` to the top as a private local variable. Move `windowManager.getDefaultDisplay().getMetrics(metrics)` to before `popupView = new...`. Move all the lines from `Rect rect =...` to `if (keyboardHeight < 100) {...}` into your getter method returning the `int keyboardHeight`. – Pierre Apr 15 '21 at 04:29
  • thanks, i tried your code, can i ask one question, i want to get keyboard height when app startup like ios does, does android version can do it too? – famfamfam Apr 15 '21 at 09:27
  • @famfamfam No, the layout must first be built and then you will be able to measure the keyboard height only afterwards. You'll have to just rethink when and why you need the keyboard height. If you want to set a views height or something, you can do it in the callback function – Pierre Apr 15 '21 at 09:31
0

(Not original Answer)

Rect r = new Rect();

View rootview = this.getWindow().getDecorView(); // this = activity

rootview.getWindowVisibleDisplayFrame(r);

Result of this is the amount of space your application uses on screen (works even when activity is not resized). Obviously remaining screen space will be used by the keyboard ( if its visible)

Found id up here: https://github.com/freshplanet/ANE-KeyboardSize/blob/master/android/src/com/freshplanet/ane/KeyboardSize/getKeyboardY.java

You can visit the Original answer

Getting the dimensions of the soft keyboard

Aman Rawat
  • 375
  • 1
  • 11
  • I'm using almost exactly the same method with only the Relative Layout that is created as "match_parent, match_parent" because I need to move the view. The problem is this calculation does not contain the autocorrect part of the soft keyboard and I'm trying to get the height of it aswell. – Furkan Yurdakul Sep 27 '18 at 08:03