Acquiring the correct posture of system input method height in Android development tutorial

  • 2021-10-16 04:55:15
  • OfStack

Problems and Solutions

In the development of Android application, there are some requirements that require us to obtain the height of input method, but the official API does not provide a similar method, so we need to implement it ourselves.

I consulted a lot of information on the Internet, but it was not ideal after I tried it.

For example, some methods calculate the height of input method by monitoring the change of layout, which is configured as follows in Activity configuration "android:windowSoftInputMode="adjustResize"" There is no problem, you can get the height of the input method correctly, because the layout will be dynamically adjusted at this time.

But when Activity is configured to "android:windowSoftInputMode="adjustNothing"" The layout will not be adjusted when the input method pops up, and the above method will save the street.

However, after a time of exploration and testing, we finally found a way to correctly calculate the height amplification method even when it is set to adjustNothing.

At the same time, I also thank this foreign friend:

GitHub Address

The method is as follows

In fact, there are only two classes, and I have also made some modifications and solved some problems, which are also posted here:

KeyboardHeightObserver.java


/**
 * The observer that will be notified when the height of 
 * the keyboard has changed
 */
public interface KeyboardHeightObserver {

 /** 
  * Called when the keyboard height has changed, 0 means keyboard is closed,
  * >= 1 means keyboard is opened.
  * 
  * @param height  The height of the keyboard in pixels
  * @param orientation The orientation either: Configuration.ORIENTATION_PORTRAIT or 
  *      Configuration.ORIENTATION_LANDSCAPE
  */
 void onKeyboardHeightChanged(int height, int orientation);
}

KeyboardHeightProvider.java


/**
 * The keyboard height provider, this class uses a PopupWindow
 * to calculate the window height when the floating keyboard is opened and closed. 
 */
public class KeyboardHeightProvider extends PopupWindow {

 /** The tag for logging purposes */
 private final static String TAG = "sample_KeyboardHeightProvider";

 /** The keyboard height observer */
 private KeyboardHeightObserver observer;

 /** The cached landscape height of the keyboard */
 private int keyboardLandscapeHeight;

 /** The cached portrait height of the keyboard */
 private int keyboardPortraitHeight;

 /** The view that is used to calculate the keyboard height */
 private View popupView;

 /** The parent view */
 private View parentView;

 /** The root activity that uses this KeyboardHeightProvider */
 private Activity activity;

 /** 
  * Construct a new KeyboardHeightProvider
  * 
  * @param activity The parent activity
  */
 public KeyboardHeightProvider(Activity activity) {
  super(activity);
  this.activity = activity;

  LayoutInflater inflator = (LayoutInflater) activity.getSystemService(Activity.LAYOUT_INFLATER_SERVICE);
  this.popupView = inflator.inflate(R.layout.keyboard_popup_window, null, false);
  setContentView(popupView);

  setSoftInputMode(LayoutParams.SOFT_INPUT_ADJUST_RESIZE | LayoutParams.SOFT_INPUT_STATE_ALWAYS_VISIBLE);
  setInputMethodMode(PopupWindow.INPUT_METHOD_NEEDED);

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

  setWidth(0);
  setHeight(LayoutParams.MATCH_PARENT);

  popupView.getViewTreeObserver().addOnGlobalLayoutListener(new OnGlobalLayoutListener() {

    @Override
    public void onGlobalLayout() {
     if (popupView != null) {
      handleOnGlobalLayout();
     }
    }
   });
 }

 /**
  * Start the KeyboardHeightProvider, this must be called after the onResume of the Activity.
  * PopupWindows are not allowed to be registered before the onResume has finished
  * of the Activity.
  */
 public void start() {

  if (!isShowing() && parentView.getWindowToken() != null) {
   setBackgroundDrawable(new ColorDrawable(0));
   showAtLocation(parentView, Gravity.NO_GRAVITY, 0, 0);
  }
 }

 /**
  * Close the keyboard height provider, 
  * this provider will not be used anymore.
  */
 public void close() {
  this.observer = null;
  dismiss();
 }

 /** 
  * Set the keyboard height observer to this provider. The 
  * observer will be notified when the keyboard height has changed. 
  * For example when the keyboard is opened or closed.
  * 
  * @param observer The observer to be added to this provider.
  */
 public void setKeyboardHeightObserver(KeyboardHeightObserver observer) {
  this.observer = observer;
 }
 
 /**
  * Get the screen orientation
  *
  * @return the screen orientation
  */
 private int getScreenOrientation() {
  return activity.getResources().getConfiguration().orientation;
 }

 /**
  * Popup window itself is as big as the window of the Activity. 
  * The keyboard can then be calculated by extracting the popup view bottom 
  * from the activity window height. 
  */
 private void handleOnGlobalLayout() {

  Point screenSize = new Point();
  activity.getWindowManager().getDefaultDisplay().getSize(screenSize);

  Rect rect = new Rect();
  popupView.getWindowVisibleDisplayFrame(rect);

  // REMIND, you may like to change this using the fullscreen size of the phone
  // and also using the status bar and navigation bar heights of the phone to calculate
  // the keyboard height. But this worked fine on a Nexus.
  int orientation = getScreenOrientation();
  int keyboardHeight = screenSize.y - rect.bottom;
  
  if (keyboardHeight == 0) {
   notifyKeyboardHeightChanged(0, orientation);
  }
  else if (orientation == Configuration.ORIENTATION_PORTRAIT) {
   this.keyboardPortraitHeight = keyboardHeight; 
   notifyKeyboardHeightChanged(keyboardPortraitHeight, orientation);
  } 
  else {
   this.keyboardLandscapeHeight = keyboardHeight; 
   notifyKeyboardHeightChanged(keyboardLandscapeHeight, orientation);
  }
 }

 private void notifyKeyboardHeightChanged(int height, int orientation) {
  if (observer != null) {
   observer.onKeyboardHeightChanged(height, orientation);
  }
 }
}

Usage

Here, the use in Activity is used as an example.

Implementation interface

With the introduction of these two classes, the interface KeyboardHeightObserver is implemented in the current Activity:


@Override
public void onKeyboardHeightChanged(int height, int orientation) {
 String or = orientation == Configuration.ORIENTATION_PORTRAIT ? "portrait" : "landscape";
 Logger.d(TAG, "onKeyboardHeightChanged in pixels: " + height + " " + or);
}

Define and initialize

Defines member variables in the current Activity and initializes them in onCreate ()


private KeyboardHeightProvider mKeyboardHeightProvider;

@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
 ...
 mKeyboardHeightProvider = new KeyboardHeightProvider(this);
 new Handler().post(() -> mKeyboardHeightProvider.start());
}

Life cycle processing

Once initialization is complete, we also need to process it during the lifecycle in Activity to avoid memory leaks.


@Override
protected void onResume() {
 super.onResume();
 mKeyboardHeightProvider.setKeyboardHeightObserver(this);
}

@Override
protected void onPause() {
 super.onPause();
 mKeyboardHeightProvider.setKeyboardHeightObserver(null);
}

@Override
protected void onDestroy() {
 super.onDestroy();
 mKeyboardHeightProvider.close();
}

Summarize

At this point, we can correctly obtain the height of the current input method, even if android:windowSoftInputMode="adjustNothing" It can also be obtained correctly, which is the strength of this method. With this method, an interface like WeChat chat can be realized, and input boxes and expression boxes can be switched smoothly.


Related articles: