47

When we have an EditText and it loses focus (to an element that doesn't need a keyboard), should the soft keyboard hide automatically or are we supposed to hide it ourselves?

I'm moving the focus from an AutoCompleteSearchView (which should behave like an EditText I guess) to a Button, requestFocus() returns true, but the keyboard doesn't hide.

fweigl
  • 19,982
  • 18
  • 97
  • 188

10 Answers10

68

Best way is to set a OnFocusChangeListener for the EditText, and then add the code to the the keyboard into the OnFocusChange method of the listener. Android will then automatically close the keyboard when the EditText loses focus.

Something like this in your OnCreate method:

EditText editText = (EditText) findViewById(R.id.textbox);
OnFocusChangeListener ofcListener = new MyFocusChangeListener();
editText.setOnFocusChangeListener(ofcListener);

and then add the class:

private class MyFocusChangeListener implements OnFocusChangeListener {

    public void onFocusChange(View v, boolean hasFocus){

        if(v.getId() == R.id.textbox && !hasFocus) {

            InputMethodManager imm =  (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE);
            imm.hideSoftInputFromWindow(v.getWindowToken(), 0);

        }
    }
}
leedsunited92
  • 542
  • 5
  • 12
  • 19
    It then has to be done for each editText component on the application? isn't there any global setting to define this behavior? If not and there are many editTexts in the app, I suggest to extend the editText class, add the listener to the extended class and use the modified class along the application – GyRo Feb 02 '15 at 11:42
  • 13
    how to make it automatically ? If i have 150 through 25 forms, i dont want to do it each time... This feature should be obvious. – Loenix May 26 '16 at 08:20
  • I had to add the following to the root view (in fragment): rootView.setFocusable(true); rootView.setFocusableInTouchMode(true); rootView.setClickable(true); – beee Feb 20 '19 at 08:49
8

Android will not hide the keyboard for you. If you want the keyboard to hide when your EditText loses focus, try using a method like this on that event:

private void hideKeypad() {
    EditText edtView = (EditText) findViewById(R.id.e_id);

    InputMethodManager imm = (InputMethodManager)getSystemService(Context.INPUT_METHOD_SERVICE);
    imm.hideSoftInputFromWindow(edtView.getWindowToken(), 0);
}
Steven Byle
  • 12,579
  • 4
  • 41
  • 57
stinepike
  • 50,967
  • 14
  • 89
  • 108
7

Try this

 /**
 * Hide keyboard on touch of UI
 */
public void hideKeyboard(View view) {

    if (view instanceof ViewGroup) {

        for (int i = 0; i < ((ViewGroup) view).getChildCount(); i++) {

            View innerView = ((ViewGroup) view).getChildAt(i);

            hideKeyboard(innerView);
        }
    }
    if (!(view instanceof EditText)) {

        view.setOnTouchListener(new OnTouchListener() {
            @Override
            public boolean onTouch(View v, MotionEvent event) {
                hideSoftKeyboard(v);
                return false;
            }

        });
    }

}

/**
 * Hide keyboard while focus is moved
 */
public void hideSoftKeyboard(View view) {
    if (view != null) {
        InputMethodManager inputManager = (InputMethodManager) contentsContext_
                .getSystemService(Context.INPUT_METHOD_SERVICE);
        if (inputManager != null) {
            if (android.os.Build.VERSION.SDK_INT < 11) {
                inputManager.hideSoftInputFromWindow(view.getWindowToken(),
                        0);
            } else {
                if (this.getCurrentFocus() != null) {
                    inputManager.hideSoftInputFromWindow(this
                            .getCurrentFocus().getWindowToken(),
                            InputMethodManager.HIDE_NOT_ALWAYS);
                }
                view.clearFocus();
            }
            view.clearFocus();
        }
    }
}
Navaneeth
  • 843
  • 7
  • 11
4

Try this, May be it will solve your problem.

private void hideKeyboard() {
    InputMethodManager mImMan = (InputMethodManager)getSystemService(Context.INPUT_METHOD_SERVICE);
    mImMan.hideSoftInputFromWindow(mYourEdttxtName.getWindowToken(), 0);
}

You can find more information from here.

Community
  • 1
  • 1
Dipak Keshariya
  • 21,618
  • 18
  • 74
  • 127
3

You can override the dispatchTouchEvent method to achieve it:

@Override
public boolean dispatchTouchEvent(MotionEvent event) {

    if (event.getAction() == MotionEvent.ACTION_DOWN) {

        /**
         * It gets into the above IF-BLOCK if anywhere the screen is touched.
         */

        View v = getCurrentFocus();
        if ( v instanceof EditText) {


            /**
             * Now, it gets into the above IF-BLOCK if an EditText is already in focus, and you tap somewhere else
             * to take the focus away from that particular EditText. It could have 2 cases after tapping:
             * 1. No EditText has focus
             * 2. Focus is just shifted to the other EditText
             */

            Rect outRect = new Rect();
            v.getGlobalVisibleRect(outRect);
            if (!outRect.contains((int)event.getRawX(), (int)event.getRawY())) {
                v.clearFocus();
                InputMethodManager imm = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE);
                imm.hideSoftInputFromWindow(v.getWindowToken(), 0);
            }
        }
    }
    return super.dispatchTouchEvent( event );
}

Bonus: In case of an EditText gaining focus, the order of event triggered is:

  1. onFocusChange() of another EditText is called (if that other edittext is losing focus)
  2. ACTION_DOWN is called
  3. Finally, onFocusChange() method of that EditText will get called.
Akeshwar Jha
  • 4,190
  • 5
  • 42
  • 85
2

Solution to this problem has already been found here.
It is using DispatchTouchEvent on activity and does not hook every EditText to either FocusChange or Touch event.
It is much better solution.

My Xamarin implementation is as follows:

public override bool DispatchTouchEvent(MotionEvent ev)
    {
        if (ev.Action == MotionEventActions.Down)
        {
            var text = CurrentFocus as EditText;
            if (text != null)
            {
                var outRect = new Rect();
                text.GetGlobalVisibleRect(outRect);
                if (outRect.Contains((int) ev.RawX, (int) ev.RawY)) return base.DispatchTouchEvent(ev);
                text.ClearFocus();
                HideSoftKeyboard();
            }
        }
        return base.DispatchTouchEvent(ev);
    }

protected void HideSoftKeyboard()
    {
        var inputMethodManager = (InputMethodManager) GetSystemService(InputMethodService);
        inputMethodManager.HideSoftInputFromWindow(CurrentFocus.WindowToken, 0);
    }
Community
  • 1
  • 1
Hrvoje Matić
  • 1,061
  • 11
  • 11
1

Just create one static method

public static void touchScreenAndHideKeyboardOnFocus(View view, final Activity activity) {

    if (view instanceof EditText) {
        view.setOnFocusChangeListener(new View.OnFocusChangeListener() {
            @Override
            public void onFocusChange(View v, boolean hasFocus) {
                if (!hasFocus) {
                    if(activity != null) {
                       InputMethodManager inputMethodManager = (InputMethodManager) activity.getSystemService(Activity.INPUT_METHOD_SERVICE);
                       if (activity.getCurrentFocus() != null) {
                          inputMethodManager.hideSoftInputFromWindow(activity.getCurrentFocus().getWindowToken(), InputMethodManager.HIDE_NOT_ALWAYS);
                       }
                    }
                }
            }
        });
    }

    if (view instanceof ViewGroup) {
        for (int i = 0; i < ((ViewGroup) view).getChildCount(); i++) {
            View innerView = ((ViewGroup) view).getChildAt(i);
            touchScreenAndHideKeyboardOnFocus(innerView, activity);
        }
    }
}

view is a root view for your layout.. but be careful, if you have another focus listener in your edittext..

0

my problem solved with this code (in Fragment)

LinearLayout linearLayoutApply=(LinearLayout)rootView.findViewById(id.LinearLayoutApply);

    linearLayoutApply.setOnFocusChangeListener(new View.OnFocusChangeListener() {
        @Override
        public void onFocusChange(View v, boolean hasFocus) {
            if(hasFocus)
            {
                hideKeyBoard(v);
            }
        }
    });

hideKeyBoard

 public void hideKeyBoard(View v)
{
    InputMethodManager imm=(InputMethodManager) getActivity().getSystemService(Context.INPUT_METHOD_SERVICE);
    imm.hideSoftInputFromWindow(getActivity().getCurrentFocus().getWindowToken(), 0);
}
Kishor N R
  • 1,371
  • 1
  • 14
  • 21
0

This works for me:

InputMethodManager imm = (InputMethodManager) context.getSystemService(Context.INPUT_METHOD_SERVICE);
imm.toggleSoftInput(InputMethodManager.HIDE_IMPLICIT_ONLY, 0);
0

Just wanted to add a Kotlin simplified version to the bunch of answers:

// onCreateView of the Fragment
myAutocompleteText.apply {
    setAdapter(adapter) // whatever the adapter is
    setOnFocusChangeListener { _, hasFocus ->
        if (hasFocus) requireActivity().closeKeyboard() // if called from an Activity instead of a Fragment, just replace it's reference instead of requireActivity()
    }
}


/**
 * Hides the keyboard if possible.
 */
object KeyboardUtils {
    fun Activity.hideKeyboard() {
        if (currentFocus != null) {
            (getSystemService(Context.INPUT_METHOD_SERVICE) as? InputMethodManager)?.hideSoftInputFromWindow(currentFocus!!.windowToken, 0)
        }
    }
}
GoRoS
  • 4,445
  • 2
  • 37
  • 57