Show/Hide Soft Keyboard event in Fragment

2019-02-27 09:42发布

问题:

There are a lot of posts about finding a show/hide soft keyboard event. I find myself in a situation where I need to change an icon based on the soft key status, in a fragment.

I tried to implement onMeasure, but I can't override that in my fragment. Is there a (relative) painless way of getting a clean show/hide soft keyboard event in my fragment or should I abondon ship?

回答1:

Sadly but true - android do not have native on software keyboard show event.

One the way handle fact that keyboard is hidden is to check entered symbols and back button press (for example textEdit will receive even back button) - but it is not flexible enough solution.

Another the possible solutions is: Override onMeasure in activity and then notify observers (pattern Observer) - for example fragments. Fragment should subscribe and unsubscribe himself onPause onResume events. Something like that for activity code:

private class DialogActivityLayout extends LinearLayout {

        public DialogActivityLayout(Context context, AttributeSet attributeSet) {
            super(context, attributeSet);
            LayoutInflater inflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
            inflater.inflate(R.layout.activity_dialog, this);
        }

        @Override
        protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
            final int proposedHeight = MeasureSpec.getSize(heightMeasureSpec);
            final int actualHeight = getHeight();

            /* Layout loaded */
            if (actualHeight == 0 || proposedHeight == actualHeight) {
                super.onMeasure(widthMeasureSpec, heightMeasureSpec);
                return;
            }

            if (proposedHeight > actualHeight) {
                DialogActivity.this.onKeyboardHide();
            } else {
                DialogActivity.this.onKeyboardShow();
            }
            super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        }
    }

I'm not sure but as I remember it works only for LinearLayout and of course activity should have adjustResize flag set (programmatically or in manifeset)

Another (I thing better approach) is to as here with globalTree observer

SoftKeyboard open and close listener in an activity in Android?



回答2:

After some struggle I was able to abstract a method that I can call whenever I need to show or hide the keyboard without using the SHOW_FORCED that I saw in many answers where the result would be the keyboard open even on a new activity without text input.

I used this code inside onGlobalLayout to check if the keyboard is open or not and then in my method I decide if I want open or close.

Here is the code:

To check if it's open or not:

private static boolean isKeyboardVisible(Activity activity) {
    Rect r = new Rect();
    View contentView = activity.findViewById(android.R.id.content);
    contentView.getWindowVisibleDisplayFrame(r);
    int screenHeight = contentView.getRootView().getHeight();
    int keypadHeight = screenHeight - r.bottom;

    return (keypadHeight > screenHeight * 0.15);
}

To perform the action that I need (here is where I call the above method):

public static void toggleKeyboard(final Activity activity, final boolean showKeyboard) {
    final InputMethodManager imm =
            (InputMethodManager) activity.getSystemService(Context.INPUT_METHOD_SERVICE);

    // The Handler is needed because the method that checks if the keyboard
    // is open need some time to get the updated value from the activity,
    // e.g. when my activity return to foreground. 
    new Handler().postDelayed(new Runnable() {
        public void run() {
            // This 'if' just check if my app still in foreground 
            // when the code is executed to avoid any problem.
            // I've leave out of the answer to keep short, you may use your own.
            if(Tools.isAppInForeground(activity)) {
                // Check the keyboard.
                boolean isVisible = isKeyboardVisible(activity);

                // If I want to show the keyboard and it's not visible, show it!
                if (showKeyboard && !isVisible) {
                    imm.toggleSoftInput(InputMethodManager.SHOW_IMPLICIT,
                            InputMethodManager.HIDE_IMPLICIT_ONLY);

                // If I want to hide and the keyboard is visible, hide it!
                } else if (!showKeyboard && isVisible) {
                    imm.toggleSoftInput(InputMethodManager.HIDE_IMPLICIT_ONLY, 0);
                }
            }
        }
    }, 100);

}

To use, I just call like this:

toggleKeyboard(myactivity, true); // show
// or
toggleKeyboard(myactivity, false); // hide