Problems and Solutions

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

Looked up a lot of information on the Internet, tried after all not ideal.

Such as through some methods to monitor the change of the layout to calculate the height of the input method, this method in the configuration of the Activity is configured to “android: windowSoftInputMode =” adjustResize “” when there is no problem, can obtain the height of the input method correctly, Because the layout does adjust dynamically at this point.

But when the Activity is configured to “android: windowSoftInputMode =” adjustNothing “” when, when ime pop-up layout adjustment, will rush toward street above way.

After some exploring and testing, I finally found a way to correctly calculate the adjustNothing method even when it is set to adjustNothing.

Also thanks to this foreign friend: GitHub address

In fact, there are only two classes. 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);
}
Copy the code
  • 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(a) {
                    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(a) {

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

        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); }}}Copy the code

Method of use

Here is an example of how to use an Activity.

Implementing an interface

With these two classes introduced, the KeyboardHeightObserver interface 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);
}
Copy the code

Define and initialize

Member variables are defined in the current Activity and initialized in onCreate()

private KeyboardHeightProvider mKeyboardHeightProvider;

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

Life cycle processing

Once the initialization is complete, we also need to process it during the Activity’s life cycle to avoid memory leaks.

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

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

@Override
protected void onDestroy(a) {
    super.onDestroy();
    mKeyboardHeightProvider.close();
}
Copy the code

conclusion

At this time we can get the height of the current input method correctly, even if the android: windowSoftInputMode = “adjustNothing” can also access to the right, this is the power of this approach, the use of this method can achieve similar WeChat chat interface, such as smooth switch input box, Emoticons, etc.

For more questions, please refer to my other Android-related blog: My blog address