423

Ok everyone knows that to hide a keyboard you need to implement:

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

But the big deal here is how to hide the keyboard when the user touches or selects any other place that is not an EditText or the softKeyboard?

I tried to use the onTouchEvent() on my parent Activity but that only works if user touches outside any other view and there is no scrollview.

I tried to implement a touch, click, focus listener without any success.

I even tried to implement my own scrollview to intercept touch events but I can only get the coordinates of the event and not the view clicked.

Is there a standard way to do this?? in iPhone it was really easy.

Gowtham Subramaniam
  • 3,358
  • 2
  • 19
  • 31
htafoya
  • 18,261
  • 11
  • 80
  • 104
  • Well I realized that the scrollview was not really the problem, but the labels that are there. The view is a vertical layout with something as: TextView, EditText,TextView,EditText, etc.. and the textViews won't let the edittext to loose focus and hide the keyboard – htafoya Nov 12 '10 at 14:30
  • You can find a solution for `getFields()` here: http://stackoverflow.com/questions/7790487/method-to-get-all-edittexts-in-a-view – Reto Oct 20 '11 at 19:17
  • Keyboard can be closed by pressing return button, so I'd say it's questionable whether this is worth the effort – gerrytan Nov 05 '13 at 09:37
  • 5
    I found this answer: http://stackoverflow.com/a/28939113/2610855 The best one. – Loenix Sep 06 '15 at 09:56

49 Answers49

619

The following snippet simply hides the keyboard:

public static void hideSoftKeyboard(Activity activity) {
    InputMethodManager inputMethodManager = 
        (InputMethodManager) activity.getSystemService(
            Activity.INPUT_METHOD_SERVICE);
    if(inputMethodManager.isAcceptingText()){
        inputMethodManager.hideSoftInputFromWindow(
                activity.getCurrentFocus().getWindowToken(),
                0
        );
    }
}

You can put this up in a utility class, or if you are defining it within an activity, avoid the activity parameter, or call hideSoftKeyboard(this).

The trickiest part is when to call it. You can write a method that iterates through every View in your activity, and check if it is an instanceof EditText if it is not register a setOnTouchListener to that component and everything will fall in place. In case you are wondering how to do that, it is in fact quite simple. Here is what you do, you write a recursive method like the following, in fact you can use this to do anything, like setup custom typefaces etc... Here is the method

public void setupUI(View view) {

    // Set up touch listener for non-text box views to hide keyboard.
    if (!(view instanceof EditText)) {
        view.setOnTouchListener(new OnTouchListener() {
            public boolean onTouch(View v, MotionEvent event) {
                hideSoftKeyboard(MyActivity.this);
                return false;
            }
        });
    }

    //If a layout container, iterate over children and seed recursion.
    if (view instanceof ViewGroup) {
        for (int i = 0; i < ((ViewGroup) view).getChildCount(); i++) {
            View innerView = ((ViewGroup) view).getChildAt(i);
            setupUI(innerView);
        }
    }
}

That is all, just call this method after you setContentView in your activity. In case you are wondering what parameter you would pass, it is the id of the parent container. Assign an id to your parent container like

<RelativeLayoutPanel android:id="@+id/parent"> ... </RelativeLayout>

and call setupUI(findViewById(R.id.parent)), that is all.

If you want to use this effectively, you may create an extended Activity and put this method in, and make all other activities in your application extend this activity and call its setupUI() in the onCreate() method.

If you use more than 1 activity define common id to parent layout like <RelativeLayout android:id="@+id/main_parent"> ... </RelativeLayout>

Then extend a class from Activity and define setupUI(findViewById(R.id.main_parent)) Within its OnResume() and extend this class instead of ``Activity in your program


Here is a Kotlin version of the above function:

@file:JvmName("KeyboardUtils")

fun Activity.hideSoftKeyboard() {
    currentFocus?.let {
        val inputMethodManager = ContextCompat.getSystemService(this, InputMethodManager::class.java)!!
        inputMethodManager.hideSoftInputFromWindow(it.windowToken, 0)
    }
}
starball
  • 20,030
  • 7
  • 43
  • 238
Navneeth G
  • 7,255
  • 1
  • 15
  • 18
  • It's a great solution but it has a lack, at least for me. When i touch again the EditText after closed it (pressing outside the EditText) the cursor is reset to the previous position (not where i have touched). Any ideas to solve thi mini bug? – user1709805 May 24 '13 at 14:51
  • It works gr8 the keyboard gets disappeared anywhere throughout application but the issue I am facing is the focus still remains on the EditTextView , eventhough the keyboard is dismissed. Any solution for this issue? – Javal Nanda Aug 25 '13 at 12:34
  • 4
    Shouldn't be very hard? I'm out of Android programming right now, so correct me if I am wrong. You could somehow track the focused EditText at any moment, and just request it to lose it's focus during an OnTouchEvent ? – Navneeth G Aug 26 '13 at 05:05
  • 27
    Not sure if anyone else has run across this issue, but this causes the app to crash when you call hideSoftKeyboard if nothing is focused. You can solve this by surrounding the second line of the method with `if(activity.getCurrentFocus() != null) {...}` – Frank Cangialosi Nov 02 '13 at 05:13
  • I think hideSoftKeyboard(); should be hideSoftKeyboard(YourActivity.this); in public boolean onTouch. Anyway this snippet helped me a lot!!!!! Thank you! – Theodoros80 Feb 23 '14 at 20:55
  • 14
    The problem with this approach is that it assumes that all other views won't ever need to set an `OnTouchListener` for them. You could just set that logic in a `ViewGroup.onInterceptTouchEvent(MotionEvent)` to a root view. – Alex.F Apr 03 '14 at 15:46
  • Works great. But you may need to add a test for avoid a potential NPE when "getCurrentFocus()" may return null, if no field has the focus when the form initially loads. if (activity.getCurrentFocus()!=null) { inputMethodManager.hideSoftInputFromWindow(activity.getCurrentFocus().getWindowToken(), 0); } – angryITguy Sep 24 '15 at 05:07
  • Thanks for this excellent solution! We used it but found that sometimes UI elements wouldn't get the touch events if dismissing the keyboard moved them, so we wrapped the call to `hideSoftKeyboard` in `v.post(new Runnable() { public void run() { hideSoftKeyboard(MyActivity.this); } });` – Lenny T Jan 29 '16 at 20:04
  • Since touch events are bubbling up the view hierarchy, I just added it to my root RelativeLayout. – David Apr 15 '16 at 20:28
  • 2
    Not working when I click other controls keeping the keyboard opened – mohitum May 05 '16 at 07:57
  • Its work well up to Android Naught . There is one problem , the edittext focus still remains even the keyboard got hide – Nikhil Jun 02 '17 at 10:24
  • It creates a new `OnTouchListener` for each view. If there are 100 views, you will create 100 instances of the same `OnTouchListener`. Isn't it better just to create one `OnTouchListener` and set them to all views? I did that, and it worked the same. – Damn Vegetables Jun 15 '18 at 07:59
  • https://stackoverflow.com/a/54308582/2064565 This answer worked for me. – Neeraj Aug 09 '19 at 10:19
  • It works, but in NestedScrollView it hides keyboard when scroll. – CoolMind Feb 09 '23 at 16:56
335

You can achieve this by doing the following steps:

  1. Make the parent view(content view of your activity) clickable and focusable by adding the following attributes

        android:clickable="true" 
        android:focusableInTouchMode="true" 
    
  2. Implement a hideKeyboard() method

        public void hideKeyboard(View view) {
            InputMethodManager inputMethodManager =(InputMethodManager)getSystemService(Activity.INPUT_METHOD_SERVICE);
            inputMethodManager.hideSoftInputFromWindow(view.getWindowToken(), 0);
        }
    
  3. Lastly, set the onFocusChangeListener of your edittext.

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

As pointed out in one of the comments below, this might not work if the parent view is a ScrollView. For such case, the clickable and focusableInTouchMode may be added on the view directly under the ScrollView.

vida
  • 4,459
  • 1
  • 22
  • 27
  • 19
    I like this answer a lot. One thing to note is that this didn't work for me when adding `clickable` and `focusableInTouchMode` to my root `ScrollView` element. I had to add to the direct parent of my `EditText` which was a `LinearLayout`. – Adam Johns Aug 29 '14 at 16:42
  • @AdamJohns +1 Tested that scenario and you are correct. I think the reason for this is that ScrollView also handles the touch event. I'll add it as note to the answer. – vida Sep 11 '14 at 16:08
  • 11
    Worked perfectly for me. Though, if you have two edittext widgets, you need to make sure that you handle onfocus to both of them correctly otherwise you will be toggling keyboard hiding unnecessarily. – Marka A Mar 23 '15 at 23:44
  • 1
    @MarkaA I had no trouble with this, When I click on the other EditText, keyboard stays, if clicked on the background it hides like it should. I did have some other handling at onFocusChange, just changing the background of the EditText when it has focus, nothing fency. – CularBytes Jul 02 '15 at 12:17
  • 1
    This works well for me in most scenarios. I am having 8 edittexts in my fragment & I have implemented OnFocusChangeListener for each in which I dismiss the keyboard. I also have a save button on the toolbar & on clicking on it I again dismiss the keyboard. But on clicking Save, the keyboard hides, I pop the fragment from the backstack & when my prev fragment is seen, the keyboard pops up again. Any solution for this. Plz help. – AndroidGuy Jan 12 '16 at 07:35
  • 1
    Does not work for me. Does the content view needs to have a background or something for this to work? My content view is `android.support.v7.widget.ContentFrameLayout`, Android 6.0.1 – sydd Jul 13 '16 at 21:43
  • 1
    Works but switching from one EditText to another will make the keyboard hide and pop (noticable when watching the triangle or back button) – Nikola Sep 15 '17 at 12:52
  • tried this solution works fine, but if I have multiple edit text in activity, I can see some kind of flicker effect which is really annoying. How to i get rid of it? – Shrikant Dec 20 '17 at 07:13
  • This won't work if user clicked on focusable children. – atjua May 04 '18 at 08:12
  • i like this answer.. you need to set clickable and focusableInTouchMode in your parent layout like ScrollView, LinearLayout, ConstraintLayout – Rio Adetya Rizky Jul 22 '18 at 08:45
  • 7
    @Shrikant - I was seeing flicker too with multiple edit texts. I just set the onFocusChangeListener on the parent instead of on each edit text, and switched the condition to say if (hasFocus) { hideKeyboard(v); } Haven't noticed flickering anymore switching btwn edit texts. – manisha Sep 05 '18 at 23:46
  • i'm not sure, when i use only this `android:clickable="true" android:focusableInTouchMode="true"` at the parent view then the soft keyboard not appears – Yohanim Sep 27 '20 at 14:55
  • 1
    android:clickable="true" will mess up the accessibility mode. It will notified the user "double click to activate" in accessibility mode – NOT_A_PROGRAMMER Jan 12 '21 at 18:30
  • Remember to also add `android:importantForAccessibility="no"` to the parent/background view to not make it focusable in talkback. – jhavatar Jul 05 '23 at 11:04
146

Just override below code in Activity

 @Override
public boolean dispatchTouchEvent(MotionEvent ev) {
    if (getCurrentFocus() != null) {
        InputMethodManager imm = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE);
        imm.hideSoftInputFromWindow(getCurrentFocus().getWindowToken(), 0);
    }
    return super.dispatchTouchEvent(ev);
}

UPDATE:

In case someone needs the Kotlin version of this answer:

override fun dispatchTouchEvent(ev: MotionEvent?): Boolean {
    if (currentFocus != null) {
        val imm = activity!!.getSystemService(Context.INPUT_METHOD_SERVICE) as InputMethodManager
        imm.hideSoftInputFromWindow(activity!!.currentFocus!!.windowToken, 0)
    }
    return super.dispatchTouchEvent(ev)
}
RedGlyph
  • 11,309
  • 6
  • 37
  • 49
sumit sonawane
  • 1,525
  • 1
  • 9
  • 15
  • 11
    Simple solution, Add in activity and it will take care of fragment also – Rohit Maurya Mar 22 '19 at 08:48
  • 1
    Another solution is to create BaseActivity and extend it in all Activities – sumit sonawane Mar 28 '19 at 06:38
  • 1
    Although a nice and clean solution, if we have an input inside a scrollable view, it will not scroll when the keyboard is open, but instead dismiss the keyboard. – angelos_lex Apr 15 '20 at 08:18
  • 19
    Nice, but it was too good to be true: simple, very short, and it worked ... alas, there is a problem: when the keyboard is displayed, each time we touch the EditText which asked the keyboard, it goes down and up automatically. – Chrysotribax Apr 22 '20 at 18:32
  • this will unfortunately hide the keyboard on TOUCH event, so even if you just want to scroll or do a mistouch it's going to hide – TootsieRockNRoll May 14 '20 at 11:11
  • Follow up on @Chrysotribax answer: It also does not work well with multiple edittexts. – Hank Chan Jan 20 '22 at 19:30
  • 1
    This answer solves the problem of @Chrysotribax: https://stackoverflow.com/a/36889384/3964537 – Megaetron Mar 15 '22 at 18:35
66

I find the accepted answer a bit complicated.

Here's my solution. Add an OnTouchListener to your main layout, ie.:

findViewById(R.id.mainLayout).setOnTouchListener(this)

and put the following code in the onTouch method.

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

This way you don't have to iterate over all views.

Gustavo Barbosa
  • 1,340
  • 1
  • 17
  • 27
roepit
  • 1,123
  • 9
  • 13
  • will this work if you click the title bar of the app? – user1506104 Apr 26 '17 at 09:59
  • 1
    This works perfectly for hiding the keyboard! Note that this doesn't actually unfocus the EditText, it just hides the keyboard. To also unfocus the EditText, add e.g. `android:onClick="stealFocusFromEditTexts"` to the xml of the parent view and then `public void stealFocusFromEditTexts(View view) {}` to its activity. The on-click method doesn't need to do anything, it just has to exist for the parent view to be focusable/selectable, which is necessary for stealing the focus from the child EditText – Jacob R Aug 30 '17 at 21:20
44

I got one more solution to hide the keyboard by:

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

Here pass HIDE_IMPLICIT_ONLY at the position of showFlag and 0 at the position of hiddenFlag. It will forcefully close the soft keyboard.

JJD
  • 50,076
  • 60
  • 203
  • 339
Saurabh Pareek
  • 7,126
  • 4
  • 28
  • 29
  • 3
    sorry, but this method is toggle, so if keyboard state is already closed, it will show the keyboard – HendraWD Oct 18 '16 at 08:51
20

A more Kotlin & Material Design way using TextInputEditText (this approach is also compatible with EditTextView)...

1.Make the parent view(content view of your activity/fragment) clickable and focusable by adding the following attributes

android:focusable="true"
android:focusableInTouchMode="true"
android:clickable="true"

2.Create an extension for all View (inside a ViewExtension.kt file for example) :

fun View.hideKeyboard(){
    val inputMethodManager = context.getSystemService(Activity.INPUT_METHOD_SERVICE) as InputMethodManager
    inputMethodManager.hideSoftInputFromWindow(this.windowToken, 0)
}

3.Create a BaseTextInputEditText that inherit of TextInputEditText. Implement the method onFocusChanged to hide keyboard when the view is not focused :

class BaseTextInputEditText(context: Context?, attrs: AttributeSet?) : TextInputEditText(context, attrs){
    override fun onFocusChanged(focused: Boolean, direction: Int, previouslyFocusedRect: Rect?) {
        super.onFocusChanged(focused, direction, previouslyFocusedRect)
        if (!focused) this.hideKeyboard()
    }
}

4.Just call your brand new custom view in your XML :

<android.support.design.widget.TextInputLayout
        android:id="@+id/textInputLayout"
        ...>

        <com.your_package.BaseTextInputEditText
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            ... />

    </android.support.design.widget.TextInputLayout> 

That's all. No need to modify your controllers (fragment or activity) to handle this repetitive case.

Phil
  • 4,730
  • 1
  • 41
  • 39
17

Well I manage to somewhat solve the problem, I overrode the dispatchTouchEvent on my activity, there I am using the following to hide the keyboard.

 /**
 * Called to process touch screen events. 
 */
@Override
public boolean dispatchTouchEvent(MotionEvent ev) {

    switch (ev.getAction()){
        case MotionEvent.ACTION_DOWN:
            touchDownTime = SystemClock.elapsedRealtime();
            break;

        case MotionEvent.ACTION_UP:
            //to avoid drag events
            if (SystemClock.elapsedRealtime() - touchDownTime <= 150){  

                EditText[] textFields = this.getFields();
                if(textFields != null && textFields.length > 0){

                    boolean clickIsOutsideEditTexts = true;

                    for(EditText field : textFields){
                        if(isPointInsideView(ev.getRawX(), ev.getRawY(), field)){
                            clickIsOutsideEditTexts = false;
                            break;
                        }
                    }

                    if(clickIsOutsideEditTexts){
                        this.hideSoftKeyboard();
                    }               
                } else {
                    this.hideSoftKeyboard();
                }
            }
            break;
    }

    return super.dispatchTouchEvent(ev);
}

EDIT: The getFields() method is just a method that returns an array with the textfields in the view. To avoid creating this array on every touch, I created an static array called sFields, which is returned at the getFields() method. This array is initialized on the onStart() methods such as:

sFields = new EditText[] {mUserField, mPasswordField};


It is not perfect, The drag event time is only based on heuristics so sometimes it doesnt hide when performing long clics, and I also finished by creating a method to get all the editTexts per view; else the keyboard would hide and show when clicking other EditText.

Still, cleaner and shorter solutions are welcome

Sergey Glotov
  • 20,200
  • 11
  • 84
  • 98
htafoya
  • 18,261
  • 11
  • 80
  • 104
  • 8
    To help others in the future, would you consider editing the code in your answer to include your `getFields()` method? It doesn't have to be exact, just an example with perhaps just some comments indicating that it returns an array of `EditText` objects. – Squonk Oct 14 '11 at 21:31
  • It is assumed that we hold code in Activity and access `EditText`s from there. Currently it is an obsolete solution. See a similar [JCutting8](https://stackoverflow.com/a/61400376/2914140) answer. – CoolMind Feb 09 '23 at 18:32
15

Override public boolean dispatchTouchEvent(MotionEvent event) in any Activity (or extend class of Activity)

@Override
public boolean dispatchTouchEvent(MotionEvent event) {
    View view = getCurrentFocus();
    boolean ret = super.dispatchTouchEvent(event);

    if (view instanceof EditText) {
        View w = getCurrentFocus();
        int scrcoords[] = new int[2];
        w.getLocationOnScreen(scrcoords);
        float x = event.getRawX() + w.getLeft() - scrcoords[0];
        float y = event.getRawY() + w.getTop() - scrcoords[1];
        
        if (event.getAction() == MotionEvent.ACTION_UP 
 && (x < w.getLeft() || x >= w.getRight() 
 || y < w.getTop() || y > w.getBottom()) ) { 
            InputMethodManager imm = (InputMethodManager)getSystemService(Context.INPUT_METHOD_SERVICE);
            imm.hideSoftInputFromWindow(getWindow().getCurrentFocus().getWindowToken(), 0);
        }
    }
 return ret;
}

Kotlin version:

 override fun dispatchTouchEvent(ev: MotionEvent?): Boolean {
        val ret = super.dispatchTouchEvent(ev)
        ev?.let { event ->
            if (event.action == MotionEvent.ACTION_UP) {
                currentFocus?.let { view ->
                    if (view is EditText) {
                        val touchCoordinates = IntArray(2)
                        view.getLocationOnScreen(touchCoordinates)
                        val x: Float = event.rawX + view.getLeft() - touchCoordinates[0]
                        val y: Float = event.rawY + view.getTop() - touchCoordinates[1]
                        //If the touch position is outside the EditText then we hide the keyboard
                        if (x < view.getLeft() || x >= view.getRight() || y < view.getTop() || y > view.getBottom()) {
                            val imm = getSystemService(Context.INPUT_METHOD_SERVICE) as InputMethodManager
                            imm.hideSoftInputFromWindow(view.windowToken, 0)
                            view.clearFocus()
                        }
                    }
                }
            }
        }
        return ret
    }

And that's all you need to do

Shohan Ahmed Sijan
  • 4,391
  • 1
  • 33
  • 39
0xh8h
  • 3,271
  • 4
  • 34
  • 55
15

Use OnFocusChangeListener.

For example:

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

Update: you also may override onTouchEvent() in your activity and check coordinates of the touch. If coordinates are outside of EditText, then hide the keyboard.

Sergey Glotov
  • 20,200
  • 11
  • 84
  • 98
14

I implemented dispatchTouchEvent in Activity to do this:

private EditText mEditText;
private Rect mRect = new Rect();
@Override
public boolean dispatchTouchEvent(MotionEvent ev) {
    final int action = MotionEventCompat.getActionMasked(ev);

    int[] location = new int[2];
    mEditText.getLocationOnScreen(location);
    mRect.left = location[0];
    mRect.top = location[1];
    mRect.right = location[0] + mEditText.getWidth();
    mRect.bottom = location[1] + mEditText.getHeight();

    int x = (int) ev.getX();
    int y = (int) ev.getY();

    if (action == MotionEvent.ACTION_DOWN && !mRect.contains(x, y)) {
        InputMethodManager input = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE);
        input.hideSoftInputFromWindow(mEditText.getWindowToken(), 0);
    }
    return super.dispatchTouchEvent(ev);
}

and I tested it, works perfect!

Jishi Chen
  • 634
  • 7
  • 17
  • works, but problem in this is that if we have more than one EditText then we need to consider that too, but I liked your answer :-) – Lalit Poptani Jun 18 '14 at 14:34
  • getActionMasked(ev) is deprecated, so now use: final int action = ev.getActionMasked(); for the first line. – Andrew Jan 04 '19 at 11:20
11

I modified the solution of Andre Luis IM I achieved this one:

I created a utility method to hide the soft keyboard the same way Andre Luiz IM did:

public static void hideSoftKeyboard(Activity activity) {
    InputMethodManager inputMethodManager = (InputMethodManager)  activity.getSystemService(Activity.INPUT_METHOD_SERVICE);
    inputMethodManager.hideSoftInputFromWindow(activity.getCurrentFocus().getWindowToken(), 0);
}

But instead of register an OnTouchListener for every view, that give a poor performance, I registered the OnTouchListener for just the root view. Since the event bubbles until it's consumed (EditText is one of the views that consumes it by default), if it arrives to the root view, it's because it wasn't consumed, so I close the soft keyboard.

findViewById(android.R.id.content).setOnTouchListener(new OnTouchListener() {
    @Override
    public boolean onTouch(View v, MotionEvent event) {
        Utils.hideSoftKeyboard(activity);
        return false;
    }
});
Fernando Camargo
  • 3,085
  • 4
  • 30
  • 49
11

I'm aware that this thread is quite old, the correct answer seems valid and there are a lot of working solutions out there, but I think the approach stated bellow might have an additional benefit regarding efficiency and elegance.

I need this behavior for all of my activities, so I created a class CustomActivity inheriting from the class Activity and "hooked" the dispatchTouchEvent function. There are mainly two conditions to take care of:

  1. If focus is unchanged and someone is tapping outside of the current input field, then dismiss the IME
  2. If focus has changed and the next focused element isn't an instance of any kind of an input field, then dismiss the IME

This is my result:

@Override
public boolean dispatchTouchEvent(MotionEvent ev) {
    if(ev.getAction() == MotionEvent.ACTION_UP) {
        final View view = getCurrentFocus();

        if(view != null) {
            final boolean consumed = super.dispatchTouchEvent(ev);

            final View viewTmp = getCurrentFocus();
            final View viewNew = viewTmp != null ? viewTmp : view;

            if(viewNew.equals(view)) {
                final Rect rect = new Rect();
                final int[] coordinates = new int[2];

                view.getLocationOnScreen(coordinates);

                rect.set(coordinates[0], coordinates[1], coordinates[0] + view.getWidth(), coordinates[1] + view.getHeight());

                final int x = (int) ev.getX();
                final int y = (int) ev.getY();

                if(rect.contains(x, y)) {
                    return consumed;
                }
            }
            else if(viewNew instanceof EditText || viewNew instanceof CustomEditText) {
                return consumed;
            }

            final InputMethodManager inputMethodManager = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE);

            inputMethodManager.hideSoftInputFromWindow(viewNew.getWindowToken(), 0);

            viewNew.clearFocus();

            return consumed;
        }
    }       

    return super.dispatchTouchEvent(ev);
}

Side note: Additionally I assign these attributes to the root view making it possible to clear focus on every input field and preventing input fields gaining focus on activity startup (making the content view the "focus catcher"):

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);

    final View view = findViewById(R.id.content);

    view.setFocusable(true);
    view.setFocusableInTouchMode(true);
}
fje
  • 845
  • 1
  • 13
  • 18
10

Instead of iterating through all the views or overriding dispatchTouchEvent.

Why Not just override the onUserInteraction() of the Activity this will make sure keyboard dismisses whenever the user taps outside of EditText.

Will work even when EditText is inside the scrollView.

@Override
public void onUserInteraction() {
    if (getCurrentFocus() != null) {
        InputMethodManager imm = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE);
        imm.hideSoftInputFromWindow(getCurrentFocus().getWindowToken(), 0);
    }
}
cyborg86pl
  • 2,597
  • 2
  • 26
  • 43
SKG
  • 774
  • 6
  • 7
8

In kotlin, we can do the following. No need to iterate all the views. It will work for fragments also.

override fun dispatchTouchEvent(ev: MotionEvent?): Boolean {
    currentFocus?.let {
        val imm: InputMethodManager = getSystemService(
            Context.INPUT_METHOD_SERVICE
        ) as (InputMethodManager)
        imm.hideSoftInputFromWindow(it.windowToken, 0)
    }
    return super.dispatchTouchEvent(ev)
}
Sai
  • 278
  • 3
  • 7
6

I liked the approach of calling dispatchTouchEvent made by htafoya, but:

  • I didn't understand the timer part (don't know why measuring the downtime should be necessary?)
  • I don't like to register/unregister all EditTexts with every view-change (could be quite a lot of viewchanges and edittexts in complex hierarchies)

So, I made this somewhat easier solution:

@Override
public boolean dispatchTouchEvent(final MotionEvent ev) {
    // all touch events close the keyboard before they are processed except EditText instances.
    // if focus is an EditText we need to check, if the touchevent was inside the focus editTexts
    final View currentFocus = getCurrentFocus();
    if (!(currentFocus instanceof EditText) || !isTouchInsideView(ev, currentFocus)) {
        ((InputMethodManager) getApplicationContext().getSystemService(Context.INPUT_METHOD_SERVICE))
            .hideSoftInputFromWindow(getCurrentFocus().getWindowToken(), InputMethodManager.HIDE_NOT_ALWAYS);
    }
    return super.dispatchTouchEvent(ev);
}

/**
 * determine if the given motionevent is inside the given view.
 * 
 * @param ev
 *            the given view
 * @param currentFocus
 *            the motion event.
 * @return if the given motionevent is inside the given view
 */
private boolean isTouchInsideView(final MotionEvent ev, final View currentFocus) {
    final int[] loc = new int[2];
    currentFocus.getLocationOnScreen(loc);
    return ev.getRawX() > loc[0] && ev.getRawY() > loc[1] && ev.getRawX() < (loc[0] + currentFocus.getWidth())
        && ev.getRawY() < (loc[1] + currentFocus.getHeight());
}

There is one disadvantage:

Switching from one EditText to another EditText makes the keyboard hide and reshow - in my case it's desired that way, because it shows that you switched between two input components.

Tass
  • 1,238
  • 11
  • 21
Christian R.
  • 1,528
  • 9
  • 16
  • Thanks, it's the best way! I just also added checking for event action: int action = ev.getActionMasked(); if (action == MotionEvent.ACTION_DOWN || action == MotionEvent.ACTION_POINTER_DOWN) {...} – sergey.n Sep 28 '15 at 17:32
6

Plea: I recognize I have no clout, but please take my answer seriously.

Problem: Dismiss soft keyboard when clicking away from keyboard or edit text with minimal code.

Solution: External library known as Butterknife.

One Line Solution:

@OnClick(R.id.activity_signup_layout) public void closeKeyboard() { ((InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE)).hideSoftInputFromWindow(getCurrentFocus().getWindowToken(), 0); }

More Readable Solution:

@OnClick(R.id.activity_signup_layout) 
public void closeKeyboard() {
        InputMethodManager imm = (InputMethodManager)getSystemService(Context.INPUT_METHOD_SERVICE);
        imm.hideSoftInputFromWindow(getCurrentFocus().getWindowToken(), 0);
}

Explanation: Bind OnClick Listener to the activity's XML Layout parent ID, so that any click on the layout (not on the edit text or keyboard) will run that snippet of code which will hide the keyboard.

Example: If your layout file is R.layout.my_layout and your layout id is R.id.my_layout_id, then your Butterknife bind call should look like:

(@OnClick(R.id.my_layout_id) 
public void yourMethod {
    InputMethodManager imm = (InputMethodManager)getSystemService(Context.INPUT_METHOD_SERVICE);
    imm.hideSoftInputFromWindow(getCurrentFocus().getWindowToken(), 0);
}

Butterknife Documentation Link: http://jakewharton.github.io/butterknife/

Plug: Butterknife will revolutionize your android development. Consider it.

Note: The same result can be achieved without the use of external library Butterknife. Just set an OnClickListener to the parent layout as described above.

Charles Woodson
  • 682
  • 1
  • 8
  • 22
5

its too simple, just make your recent layout clickable an focusable by this code:

android:id="@+id/loginParentLayout"
android:clickable="true"
android:focusableInTouchMode="true"

and then write a method and an OnClickListner for that layout , so that when the uppermost layout is touched any where it will call a method in which you will write code to dismiss keyboard. following is the code for both; // you have to write this in OnCreate()

 yourLayout.setOnClickListener(new View.OnClickListener(){
                @Override
                public void onClick(View view) {
                    hideKeyboard(view);
                }
            });

method called from listner:-

 public void hideKeyboard(View view) {
     InputMethodManager imm =(InputMethodManager)getSystemService(Activity.INPUT_METHOD_SERVICE);
        imm.hideSoftInputFromWindow(view.getWindowToken(), 0);
    }
swarnim dixit
  • 65
  • 1
  • 4
4

Here's another variation on fje's answer that addresses the issues raised by sosite.

The idea here is to handle both the down and the up actions in the Activity's dispatchTouchEvent method. On the down action, we make note of the currently focused view (if any) and whether the touch was inside it, saving both those bits of info for later.

On the up action, we first dispatch, to allow another view to potentially take focus. If after that, the currently focused view is the originally focused view, and the down touch was inside that view, then we leave the keyboard open.

If the currently focused view is different than the originally focused view and it's an EditText, then we also leave the keyboard open.

Otherwise we close it.

So, to sum up, this works as follows:

  • when touching inside a currently focused EditText, the keyboard stays open
  • when moving from a focused EditText to another EditText, the keyboard stays open (doesn't close/reopen)
  • when touching anywhere outside a currently focused EditText that is not another EditText, the keyboard closes
  • when long-pressing in an EditText to bring up the contextual action bar (with the cut/copy/paste buttons), the keyboard stays open, even though the UP action took place outside the focused EditText (which moved down to make room for the CAB). Note, though, that when you tap on a button in the CAB, it will close the keyboard. That may or may not be desirable; if you want to cut/copy from one field and paste to another, it would be. If you want to paste back into the same EditText, it would not be.
  • when the focused EditText is at the bottom of the screen and you long-click on some text to select it, the EditText keeps focus and therefore the keyboard opens like you want, because we do the "touch is within view bounds" check on the down action, not the up action.

    private View focusedViewOnActionDown;
    private boolean touchWasInsideFocusedView;
    
    
    @Override
    public boolean dispatchTouchEvent(MotionEvent ev) {
        switch (ev.getAction()) {
            case MotionEvent.ACTION_DOWN:
                focusedViewOnActionDown = getCurrentFocus();
                if (focusedViewOnActionDown != null) {
                    final Rect rect = new Rect();
                    final int[] coordinates = new int[2];
    
                    focusedViewOnActionDown.getLocationOnScreen(coordinates);
    
                    rect.set(coordinates[0], coordinates[1],
                            coordinates[0] + focusedViewOnActionDown.getWidth(),
                            coordinates[1] + focusedViewOnActionDown.getHeight());
    
                    final int x = (int) ev.getX();
                    final int y = (int) ev.getY();
    
                    touchWasInsideFocusedView = rect.contains(x, y);
                }
                break;
    
            case MotionEvent.ACTION_UP:
    
                if (focusedViewOnActionDown != null) {
                    // dispatch to allow new view to (potentially) take focus
                    final boolean consumed = super.dispatchTouchEvent(ev);
    
                    final View currentFocus = getCurrentFocus();
    
                    // if the focus is still on the original view and the touch was inside that view,
                    // leave the keyboard open.  Otherwise, if the focus is now on another view and that view
                    // is an EditText, also leave the keyboard open.
                    if (currentFocus.equals(focusedViewOnActionDown)) {
                        if (touchWasInsideFocusedView) {
                            return consumed;
                        }
                    } else if (currentFocus instanceof EditText) {
                        return consumed;
                    }
    
                    // the touch was outside the originally focused view and not inside another EditText,
                    // so close the keyboard
                    InputMethodManager inputMethodManager =
                            (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE);
                    inputMethodManager.hideSoftInputFromWindow(
                        focusedViewOnActionDown.getWindowToken(), 0);
                    focusedViewOnActionDown.clearFocus();
    
                    return consumed;
                }
                break;
        }
    
        return super.dispatchTouchEvent(ev);
    }
    
Andy Dennie
  • 6,012
  • 2
  • 32
  • 51
4

I find the accepted answer bit complex for this simple requirement. Here's what worked for me without any glitch.

findViewById(R.id.mainLayout).setOnTouchListener(new View.OnTouchListener() {
        @Override
        public boolean onTouch(View view, MotionEvent motionEvent) {
            InputMethodManager imm = (InputMethodManager) getSystemService(INPUT_METHOD_SERVICE);
            imm.hideSoftInputFromWindow(getCurrentFocus().getWindowToken(), 0);
            return false;
        }
    });
Joohay
  • 51
  • 7
  • 1
    If you use `NestedScrollView` or complex layouts, see the accepted answer: https://stackoverflow.com/a/11656129/2914140. You should know that other containers may consume touches. – CoolMind Feb 04 '19 at 12:45
4

This one is the easiest solution for me (and worked out by me).

This is the method to hide the keyboard.

public void hideKeyboard(View view){
        if(!(view instanceof EditText)){
            InputMethodManager inputMethodManager=(InputMethodManager)getSystemService(INPUT_METHOD_SERVICE);
            inputMethodManager.hideSoftInputFromWindow(getCurrentFocus().getWindowToken(),0);
        }
    }

now set onclick attribute of the parent layout of the activity to above method hideKeyboard either from the Design view of your XML file or writing below code in Text view of your XML file.

android:onClick="hideKeyboard"
NullByte08
  • 884
  • 10
  • 15
3

There is a simpler approach, based on iPhone same issue. Simply override the background's layout on touch event, where the edit text is contained. Just use this code in the activity's OnCreate (login_fondo is the root layout):

    final LinearLayout llLogin = (LinearLayout)findViewById(R.id.login_fondo);
    llLogin.setOnTouchListener(
            new OnTouchListener()
            {
                @Override
                public boolean onTouch(View view, MotionEvent ev) {
                    InputMethodManager imm = (InputMethodManager) mActivity.getSystemService(
                            android.content.Context.INPUT_METHOD_SERVICE);
                    imm.hideSoftInputFromWindow(mActivity.getCurrentFocus().getWindowToken(), 0);
                    return false;
                }
            });
Alex R. R.
  • 2,992
  • 1
  • 18
  • 13
3

Method for show / hide soft keyboard

InputMethodManager inputMethodManager = (InputMethodManager) currentActivity.getSystemService(Context.INPUT_METHOD_SERVICE);
    if (isShow) {
        if (currentActivity.getCurrentFocus() == null) {
            inputMethodManager.toggleSoftInput(InputMethodManager.SHOW_FORCED, 0);
        } else {
            inputMethodManager.showSoftInput(currentActivity.getCurrentFocus(), InputMethodManager.SHOW_FORCED);    
        }

    } else {
        if (currentActivity.getCurrentFocus() == null) {
            inputMethodManager.toggleSoftInput(InputMethodManager.HIDE_NOT_ALWAYS, 0);
        } else {
            inputMethodManager.hideSoftInputFromInputMethod(currentActivity.getCurrentFocus().getWindowToken(), InputMethodManager.HIDE_NOT_ALWAYS);    
        }

    }

I hope they have been useful

lalosoft
  • 134
  • 6
3

Activity

 @Override
 public boolean dispatchTouchEvent(MotionEvent ev) {
     ScreenUtils.hideKeyboard(this, findViewById(android.R.id.content).getWindowToken());
     return super.dispatchTouchEvent(ev);
 }

ScreenUtils

 public static void hideKeyboard(Context context, IBinder windowToken) {
     InputMethodManager imm = (InputMethodManager) context.getSystemService(Context.INPUT_METHOD_SERVICE);
     imm.hideSoftInputFromWindow(windowToken, InputMethodManager.HIDE_NOT_ALWAYS);
 }
Jean-François Corbett
  • 37,420
  • 30
  • 139
  • 188
icebail
  • 269
  • 2
  • 14
  • 4
    This code is simple, but it has an obvious issue: it closes the keyboard when *anywhere* is touched. That is if you tap a different location of the EditText to move the input cursor, it hides the keyboard, and the keyboard pops up again by the system. – Damn Vegetables Jun 15 '18 at 07:26
2

I got this working with a slight variant on Fernando Camarago's solution. In my onCreate method I attach a single onTouchListener to the root view but send the view rather than activity as an argument.

        findViewById(android.R.id.content).setOnTouchListener(new OnTouchListener() {           
        public boolean onTouch(View v, MotionEvent event) {
            Utils.hideSoftKeyboard(v);
            return false;
        }
    });

In a separate Utils class is...

    public static void hideSoftKeyboard(View v) {
    InputMethodManager imm = (InputMethodManager) v.getContext().getSystemService(Context.INPUT_METHOD_SERVICE); 
    imm.hideSoftInputFromWindow(v.getWindowToken(), 0);
}
AndyMc
  • 23
  • 3
2

This may be old but I got this working by implenting a custom class

public class DismissKeyboardListener implements OnClickListener {

    Activity mAct;

    public DismissKeyboardListener(Activity act) {
        this.mAct = act;
    }

    @Override
    public void onClick(View v) {
        if ( v instanceof ViewGroup ) {
            hideSoftKeyboard( this.mAct );
        }
    }       
}

public void hideSoftKeyboard(Activity activity) {
        InputMethodManager imm = (InputMethodManager)
        getSystemService(Activity.INPUT_METHOD_SERVICE);
        imm.toggleSoftInput(InputMethodManager.HIDE_IMPLICIT_ONLY, 0);
}

the best practice here is to create a Helper class and every container Relative / Linear Layouts should implement this.

**** Take note only the main Container should implement this class (For optimization) ****

and implement it like this :

Parent.setOnClickListener( new DismissKeyboardListener(this) ); 

the keyword this is for Activity. so if you are on fragment you use like getActivity();

---thumbs up if it help you... --- cheers Ralph ---

ralphgabb
  • 10,298
  • 3
  • 47
  • 56
2

I have done this way:

@Override
public boolean dispatchTouchEvent(MotionEvent ev) {
   View view = getCurrentFocus();
   if (view != null && (ev.getAction() == MotionEvent.ACTION_UP || ev.getAction() == MotionEvent.ACTION_MOVE) && view instanceof EditText && !view.getClass().getName().startsWith("android.webkit.")) {
            int scrcoords[] = new int[2];
            view.getLocationOnScreen(scrcoords);
            float x = ev.getRawX() + view.getLeft() - scrcoords[0];
            float y = ev.getRawY() + view.getTop() - scrcoords[1];
            if (x < view.getLeft() || x > view.getRight() || y < view.getTop() || y > view.getBottom())
                hideKeyboard(this);
        }
    return super.dispatchTouchEvent(ev);
}

Hide keyboard code:

public static void hideKeyboard(Activity act) {
    if(act!=null)
      ((InputMethodManager)act.getSystemService(Context.INPUT_METHOD_SERVICE)).hideSoftInputFromWindow((act.getWindow().getDecorView().getApplicationWindowToken()), 0);
  }

Done

Hiren Patel
  • 52,124
  • 21
  • 173
  • 151
2

To solve this problem what you have to do is first use setOnFocusChangeListener of that Edittext

edittext.setOnFocusChangeListener(new View.OnFocusChangeListener() {
            @Override
            public void onFocusChange(View v, boolean hasFocus) {
                if (!hasFocus) {
                    Log.d("focus", "focus loosed");
                    // Do whatever you want here
                } else {
                    Log.d("focus", "focused");
                }
            }
        });

and then what you need to do is override dispatchTouchEvent in the activity which contains that Edittext see below code

@Override
public boolean dispatchTouchEvent(MotionEvent event) {
    if (event.getAction() == MotionEvent.ACTION_DOWN) {
        View v = getCurrentFocus();
        if ( v instanceof EditText) {
            Rect outRect = new Rect();
            v.getGlobalVisibleRect(outRect);
            if (!outRect.contains((int)event.getRawX(), (int)event.getRawY())) {
                Log.d("focus", "touchevent");
                v.clearFocus();
                InputMethodManager imm = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE);
                imm.hideSoftInputFromWindow(v.getWindowToken(), 0);
            }
        }
    }
    return super.dispatchTouchEvent(event);
}

Now what will happen is when a user click outside then firstly this dispatchTouchEvent will get called which then will clear focus from the editext now your OnFocusChangeListener will get called that focus has been changed now here you can do anything which you wanted to do hope it works

Sudhanshu Gaur
  • 7,486
  • 9
  • 47
  • 94
2

I have refined the method, put the following code in some UI utility class(preferably, not necessarily) so that it can be accessed from all your Activity or Fragment classes to serve its purpose.

public static void serachAndHideSoftKeybordFromView(View view, final Activity act) {
    if(!(view instanceof EditText)) {
        view.setOnTouchListener(new View.OnTouchListener() {
            public boolean onTouch(View v, MotionEvent event) {
                hideSoftKeyboard(act);
                return false;
            }
        });
    }
    if (view instanceof ViewGroup) {
        for (int i = 0; i < ((ViewGroup) view).getChildCount(); i++) {
            View nextViewInHierarchy = ((ViewGroup) view).getChildAt(i);
            serachAndHideSoftKeybordFromView(nextViewInHierarchy, act);
        }
    }
}
public static void hideSoftKeyboard (Activity activity) {
    InputMethodManager inputMethodManager = (InputMethodManager) activity.getSystemService(Activity.INPUT_METHOD_SERVICE);
    inputMethodManager.hideSoftInputFromWindow(activity.getCurrentFocus().getWindowToken(), 0);
}

Then say for example you need to call it from activity, call it as follows;

UIutils.serachAndHideSoftKeybordFromView(findViewById(android.R.id.content), YourActivityName.this);

Notice

findViewById(android.R.id.content)

This gives us the root view of the current group(you mustn't have set the id on root view).

Cheers :)

Uzair
  • 1,529
  • 14
  • 17
2

Try to put stateHidden on as your activity windowSoftInputMode value

http://developer.android.com/reference/android/R.attr.html#windowSoftInputMode

For example for your Activity:

this.getWindow().setSoftInputMode(
    WindowManager.LayoutParams.SOFT_INPUT_STATE_HIDDEN);
JJD
  • 50,076
  • 60
  • 203
  • 339
Alex Volovoy
  • 67,778
  • 13
  • 73
  • 54
2

Just Add this code in the class @Overide

public boolean dispatchTouchEvent(MotionEvent ev) {
    View view = getCurrentFocus();
    if (view != null && (ev.getAction() == MotionEvent.ACTION_UP || ev.getAction() == MotionEvent.ACTION_MOVE) && view instanceof EditText && !view.getClass().getName().startsWith("android.webkit.")) {
        int scrcoords[] = new int[2];
        view.getLocationOnScreen(scrcoords);
        float x = ev.getRawX() + view.getLeft() - scrcoords[0];
        float y = ev.getRawY() + view.getTop() - scrcoords[1];
        if (x < view.getLeft() || x > view.getRight() || y < view.getTop() || y > view.getBottom())
            ((InputMethodManager)this.getSystemService(Context.INPUT_METHOD_SERVICE)).hideSoftInputFromWindow((this.getWindow().getDecorView().getApplicationWindowToken()), 0);
    }
    return super.dispatchTouchEvent(ev);
}
Haseeb Javed
  • 1,769
  • 17
  • 20
  • 4
    While this may answer the question, it is better to explain the essential parts of the answer and possibly what was the problem with OPs code. – pirho Dec 05 '17 at 09:51
2

you can implement View.onClickListener and override onClick method and set this onclicklistener to the Layout

ConstraintLayout constraintLayout;
public class MainActivity extends AppCompatActivity implements View.OnClickListener{
@Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        constraintLayout = findViewById(R.id.layout);
        constraintLayout.setOnClickListener(this);
}
@Override
    public void onClick(View v) {
        if(v.getId()==R.id.layout){
            InputMethodManager inm = (InputMethodManager)getSystemService(Context.INPUT_METHOD_SERVICE);
            inm.hideSoftInputFromWindow(getCurrentFocus().getWindowToken(),0);
        }
    }
Vickel
  • 7,879
  • 6
  • 35
  • 56
1

This is a slightly modified version of fje's answer which mostly worked perfectly.

This version uses ACTION_DOWN so performing a scroll action also closes the keyboard. It also doesn't propagate the event unless you click on another EditText. This means that clicking anywhere outside your EditText, even on another clickable, simply closes the keyboard.

@Override
public boolean dispatchTouchEvent(MotionEvent ev)
{
    if(ev.getAction() == MotionEvent.ACTION_DOWN)
    {
        final View view = getCurrentFocus();

        if(view != null)
        {
            final View viewTmp = getCurrentFocus();
            final View viewNew = viewTmp != null ? viewTmp : view;

            if(viewNew.equals(view))
            {
                final Rect rect = new Rect();
                final int[] coordinates = new int[2];

                view.getLocationOnScreen(coordinates);

                rect.set(coordinates[0], coordinates[1], coordinates[0] + view.getWidth(), coordinates[1] + view.getHeight());

                final int x = (int) ev.getX();
                final int y = (int) ev.getY();

                if(rect.contains(x, y))
                {
                    super.dispatchTouchEvent(ev);
                    return true;
                }
            }
            else if(viewNew instanceof EditText || viewNew instanceof CustomEditText)
            {
                super.dispatchTouchEvent(ev);
                return true;
            }

            final InputMethodManager inputMethodManager = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE);

            inputMethodManager.hideSoftInputFromWindow(viewNew.getWindowToken(), 0);

            viewNew.clearFocus();

            return true;
        }
    }
    return super.dispatchTouchEvent(ev);
}
Cadmonkey33
  • 181
  • 1
  • 5
  • Something doesn't look right here; you're assigning both `view` and `viewTmp` to `getCurrentFocus()`, so they're always going to be the same value. – Andy Dennie Sep 24 '15 at 19:09
1
@Override
    public boolean onTouchEvent(MotionEvent event) {
        InputMethodManager imm = (InputMethodManager)getSystemService(Context.
                INPUT_METHOD_SERVICE);
        imm.hideSoftInputFromWindow(getCurrentFocus().getWindowToken(), 0);
        return true;
    }
Jeffy
  • 276
  • 3
  • 9
1

Well, You can use this code, Use your Main Layout Id instead of "mainRelativeLayout"

//hide Soft keyboard on click outside  the input text
    findViewById(R.id.mainRelativeLayout).setOnClickListener(new 
View.OnClickListener() {
        @Override
        public void onClick(View v) {
            InputMethodManager im = (InputMethodManager) 
getSystemService(INPUT_METHOD_SERVICE);
            im.hideSoftInputFromWindow(getCurrentFocus().getWindowToken(), 
0);
        }

    });
vinod wakade
  • 139
  • 1
  • 1
  • 8
1

Adding on to a previous answer by @sumit sonawane , this solution will not hide the keyboard if the user is scrolling.

public long pressTime = 0;
@Override
public boolean dispatchTouchEvent(MotionEvent ev) {
    if (ev.getAction() == MotionEvent.ACTION_DOWN) {
        pressTime = System.currentTimeMillis();
    }
    else if (ev.getAction() == MotionEvent.ACTION_UP) {
        long releaseTime = System.currentTimeMillis();
        if (releaseTime-pressTime < 200) {
            if (getCurrentFocus() != null) {
                GhostTube.print("BottomNavActivity", "Touch event with keyboard detected...");
                InputMethodManager imm = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE);
                imm.hideSoftInputFromWindow(getCurrentFocus().getWindowToken(), 0);
            }
        }
    }
    return super.dispatchTouchEvent(ev);
}

Add code to your activity, will also take care of fragments.

JCutting8
  • 732
  • 9
  • 29
1

I modified the answer @navneeth-g a bit, adding null activity focus processing, avoiding multiple instance creation OnTouchListener, remove focus when keyboard is hiding, and removed hiding the keyboard when the screen scrolls, convenient on small devices.

//In general, the view parameter is root layout
fun Activity.hideKeyboardOnClickOutsideEditText(view: View) {
    // Set up touch listener for non-text box views to hide keyboard.
    var previousAction = 0
    val onTouchListener = View.OnTouchListener { v, event ->
        if (currentFocus != null
            && event.action != MotionEvent.ACTION_DOWN
            && event.action != MotionEvent.ACTION_MOVE
            && previousAction != MotionEvent.ACTION_MOVE
        ) {
            currentFocus?.clearFocus()
            v?.hideKeyboard()
        }
        previousAction = event.action
        false
    }

    if (view !is EditText) {
        view.setOnTouchListener(onTouchListener)
    }

    //If a layout container, iterate over children and seed recursion.
    if (view is ViewGroup) {
        for (i in 0 until view.childCount) {
            val innerView = view.getChildAt(i)
            hideKeyboardOnClickOutsideEditText(innerView)
        }
    }
}
   
//in root layout.xml
android:clickable="true"
android:focusable="true"
android:focusableInTouchMode="true"
Zakhar Rodionov
  • 1,398
  • 16
  • 18
1

Very difficult decisions. I suggest a simpler solution. In onViewCreated, we set a setOnClickListener for the entire View and remove focus from the EditText, as well as remove the keyboard.

Example in Fragment. Java.

@Override
public void onViewCreated(View view, Bundle savedInstanceState)
{
    super.onViewCreated(view, savedInstanceState);

    nameTextInputEditText = view.findViewById(R.id.NameTextInputEditText);

    view.setOnClickListener(v -> {
        nameTextInputEditText.clearFocus();

        InputMethodManager imm = (InputMethodManager) requireActivity().getSystemService(Activity.INPUT_METHOD_SERVICE);
        imm.hideSoftInputFromWindow(v.getWindowToken(), 0);
    });
}
Nikita
  • 11
  • 1
0

You may easily override the onKey() event in activity and fragments to hide the keyboard.

@Override
public boolean onKey(View v, int keyCode, KeyEvent event) {

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

            intiateLoginProcess();
            InputMethodManager imm = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE);
            imm.hideSoftInputFromWindow(getWindow().getCurrentFocus()
                    .getWindowToken(), 0);

            return true;
        }
    }
    return false;
}
Ajith M A
  • 3,838
  • 3
  • 32
  • 55
0

hey guys i have simple solution for this problem and this solution can be used for simple registration or login form. my solution is same as i implemented in ios setontouch listener to Main view

activity_main.xml add ID to your main relative layout android:id="@+id/mainlayout"

and add this code to your activity

  RelativeLayout mainLayout = (RelativeLayout)findViewById(R.id.mainlayout);
  mainLayout.setOnTouchListener(new OnTouchListener() {

            @Override
            public boolean onTouch(View v, MotionEvent event) {
                // TODO Auto-generated method stub
                 Log.d("Json Response", "Touch outside");
                  InputMethodManager inputMethodManager = (InputMethodManager)  MainActivity.this.getSystemService(Activity.INPUT_METHOD_SERVICE);
                    inputMethodManager.hideSoftInputFromWindow(MainActivity.this.getCurrentFocus().getWindowToken(), 0);
                return false;
            }
        });
Swap-IOS-Android
  • 4,363
  • 6
  • 49
  • 77
  • You could use "View mainLayout..." and be able to re-use it regardless of your base layout type. However, your method is still ineffective if the user taps a view that consumes touch events like buttons and spinners. – William T. Mallard Aug 03 '14 at 03:40
0

I thought this problem. first, I think that setOnTouchListener is not simple solution. so I believe dispatchTouchEvent is best simple solution.

public boolean dispatchKeyEvent(KeyEvent event) {
    if (event.getAction() == KeyEvent.ACTION_UP) {
        View v = getCurrentFocus();
        if (v instanceof EditText) {
            InputMethodManager imm = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE);
            imm.hideSoftInputFromWindow(getCurrentFocus().getWindowToken(), 0);
        }
    }
    return super.dispatchKeyEvent(event);
}

in here, a important is ACTION_UP.

I assumed EditText only show soft keyboard otherwise not show the keyboard. I have tested on Android5.0.1 (G3.cat6 of LG).

if you need drag checking, long click, ..., show comments above.

Hogun
  • 602
  • 6
  • 9
0

My solution hides keyboard on outside click in any activity, with of all edit texts. Without specifying them one by one.

First add to root view of layout xml: android:clickable="true" android:focusableInTouchMode="true"

Next, create one parent Acitvity of all activities you want to hide keyboard, and specify onResume() method:

 @Override
    protected void onResume() {
        super.onResume();
        //getting Root View that gets focus
        View rootView =((ViewGroup)findViewById(android.R.id.content)).
                getChildAt(0);
        rootView.setOnFocusChangeListener(new View.OnFocusChangeListener() {
            @Override
            public void onFocusChange(View v, boolean hasFocus) {
                if (hasFocus) {
                    hideKeyboard(AbstractActivity.this);
                }
            }
        });
    }

Extend your activity with this General activity (Inheritance power !) and that's all, every time any EditText (on any extended Activity) will lose focus, keyboard will be hidden.

P.S. hideKeyboard method :

public static void hideKeyboard(Activity context) {
    InputMethodManager inputMethodManager = (InputMethodManager) context.getSystemService(Activity.INPUT_METHOD_SERVICE);
    inputMethodManager.hideSoftInputFromWindow( context.getCurrentFocus().getWindowToken(), 0);
}

context.getCurrentFocus() doesn't need specifying specific EditText view..

Misha Akopov
  • 12,241
  • 27
  • 68
  • 82
0

You can try the way below, it works great for me :)

This way can be applied for Activity or Fragment and it's also compatible with ScrollView.

We put ScrollView as a top-level layout, declare id parentView for the LinearLayout inside and add two attributes like below:

android:id="@+id/parentView"
android:clickable="true"
android:focusableInTouchMode="true"

In code, write a function like below:

public static void hideSoftKeyboard (Activity activity) {
        InputMethodManager inputMethodManager = (InputMethodManager) activity.getSystemService(Activity.INPUT_METHOD_SERVICE);
        inputMethodManager.hideSoftInputFromWindow(activity.getCurrentFocus().getWindowToken(), 0);
    }

Then register an OnFocusChangeListener for the root view (write in onCreate method) to make all EditText in Activity affected:

parentLayout.setOnFocusChangeListener(new View.OnFocusChangeListener() {
            @Override
            public void onFocusChange(View v, boolean hasFocus) {
                if (hasFocus) {
                    hideSoftKeyboard(your_activity_name.this);
                }
            }
        });
Dang Nguyen
  • 354
  • 2
  • 9
0

I managed to hide the keyboard from inside onItemClick AutoCompleteTextView

public void onItemClick(AdapterView<?> adapterViewIn, View viewIn, int indexSelected, long arg3) {
     InputMethodManager imm = (InputMethodManager) getSystemService(viewIn.getContext().INPUT_METHOD_SERVICE);
     imm.hideSoftInputFromWindow(viewIn.getApplicationWindowToken(), 0);
     // your code HERE
}
tony gil
  • 9,424
  • 6
  • 76
  • 100
0

Other idea is to override onInterceptTouchEvent method on the root view for your Activity.

The touch event goes from the front most view on the screen (where the touch event occurred) down the stack of views calling the onTouch method until any of the views return true, indicating that the touch event was consumed. As many of the view consumes the touch event by default (that is the case of EditText or TextView, for instance), the event does not get to the Activity's root View onTouch method.

But, before do this traversal, the touch event travels another path, going from the root view down the view tree until it gets to the front most view. This traversal is done by calling onInterceptTouchEvent. If the method returns true, it intercepts the event... nahhh, but that is a little bit trick, I don't think you want to do that nor to know the details. What you need to know is that you can override this method on the root view for your Activity, and put there the code to hide the keyboard when necessary.

  • I thought of that a couple of hours ago, but my question is how to know if the coordinates of the event are of an EditText or the keyboard. In other words, how to get a view given the coordinates, because I just want to use instanceof EditText, I am trying to evade implementing this method on every class and comparing specific object bounds. – htafoya Nov 12 '10 at 17:10
0

The only working code

private var viewHeight = 0

private fun setRootViewListener() {
    binding.root.apply {
        viewTreeObserver.addOnGlobalLayoutListener {
            viewHeight = height
        }
    }
}

override fun dispatchTouchEvent(event: MotionEvent): Boolean {
    currentFocus?.let {
        if (it is EditText && event.y < viewHeight - it.measuredHeight) {
            hideKeyboard(it)
        }
    }
    return super.dispatchTouchEvent(event)
}
eggham0518
  • 121
  • 1
  • 7
0

For those who are looking for a Xamarin code for this, here you go :

  public override bool DispatchTouchEvent(MotionEvent ev)
    {
        try
        {
            View view = CurrentFocus;
            if (view != null && (ev.Action == MotionEventActions.Up || ev.Action == MotionEventActions.Move) && view is EditText && !view.Class.Name.StartsWith("android.webkit."))
            {
                int[] Touch = new int[2];
                view.GetLocationOnScreen(Touch);
                float x = ev.RawX + view.Left - Touch[0];
                float y = ev.RawY + view.Top - Touch[1];
                if (x < view.Left || x > view.Right || y < view.Top || y > view.Bottom)
                    ((InputMethodManager)GetSystemService(InputMethodService)).HideSoftInputFromWindow((Window.DecorView.ApplicationWindowToken), 0);
            }
        }
        catch (System.Exception ex)
        {
           
        }

        return base.DispatchTouchEvent(ev);
    }
FreakyAli
  • 13,349
  • 3
  • 23
  • 63
0

I have simple solution for this problem:

InputMethodManager inputMethodManager = (InputMethodManager) getActivity().getSystemService(Context.INPUT_METHOD_SERVICE);
            inputMethodManager.hideSoftInputFromWindow(getActivity().getCurrentFocus().getWindowToken(),InputMethodManager.HIDE_NOT_ALWAYS);
-1
setupUI((RelativeLayout) findViewById(R.id.activity_logsign_up_RelativeLayout));

Pass the method into your layout file. You must choose your common layout file in XML. Because, keyboard hide works with whole layout.

public void setupUI(View view) {
        // Set up touch listener for non-text box views to hide keyboard.
        if (!(view instanceof EditText)) {
            view.setOnTouchListener(new View.OnTouchListener() {
                public boolean onTouch(View v, MotionEvent event) {
                    hideSoftKeyboard(Your Context); // Pass your context
                    return false;
                }
            });
        }
        //If a layout container, iterate over children and seed recursion.
        if (view instanceof ViewGroup) {
            for (int i = 0; i < ((ViewGroup) view).getChildCount(); i++) {
                View innerView = ((ViewGroup) view).getChildAt(i);
                setupUI(innerView);
            }
        }
    }
Gowtham Subramaniam
  • 3,358
  • 2
  • 19
  • 31
-1

Go to Manifest and write: android:windowSoftInputMode="stateHidden" in activity, Like this

<activity
   android:name=".MainActivity"
   android:exported="false"
   android:windowSoftInputMode="stateHidden" />