Как скрыть мягкую клавиатуру на Android после нажатия вне EditText?

Хорошо всем известно, что для скрытия клавиатуры нужно реализовать:

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

Но главное здесь в том, как скрыть клавиатуру, когда пользователь касается или выбирает любое другое место, кроме EditText или softKeyboard?

Я попытался использовать onTouchEvent() на моем родительском Activity, но это работает только в том случае, если пользователь касается за пределами любого другого представления и нет прокрутки.

Я безуспешно пытался реализовать прослушиватель касания, щелчка, фокусировки.

Я даже попытался реализовать свой собственный scrollview для перехвата событий касания, но я могу получить только координаты события, а не вид, по которому щелкнули.

Есть стандартный способ сделать это ?? в iPhone это было действительно просто.


person htafoya    schedule 12.11.2010    source источник
comment
Хорошо, я понял, что проблема была не в просмотре прокрутки, а в ярлыках, которые там есть. Представление представляет собой вертикальный макет с чем-то вроде: TextView, EditText, TextView, EditText и т. Д., И textViews не позволят тексту редактирования потерять фокус и скрыть клавиатуру   -  person htafoya    schedule 12.11.2010
comment
Вы можете найти решение для getFields() здесь: stackoverflow.com/questions/7790487/   -  person Reto    schedule 20.10.2011
comment
Клавиатуру можно закрыть, нажав кнопку возврата, поэтому я бы сказал, что сомнительно, стоит ли это усилий.   -  person gerrytan    schedule 05.11.2013
comment
Я нашел этот ответ: stackoverflow.com/a/28939113/2610855 Самый лучший.   -  person Loenix    schedule 06.09.2015


Ответы (44)


Следующий фрагмент просто скрывает клавиатуру:

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

Вы можете поместить это в служебный класс или, если вы определяете его в действии, не использовать параметр активности или вызвать hideSoftKeyboard(this).

Самое сложное - когда это назвать. Вы можете написать метод, который выполняет итерацию каждого View в вашей деятельности, и проверяет, является ли он instanceof EditText, если он не зарегистрировал setOnTouchListener для этого компонента, и все встанет на свои места. Если вам интересно, как это сделать, на самом деле это довольно просто. Вот что вы делаете: вы пишете рекурсивный метод, подобный следующему, на самом деле вы можете использовать его для чего угодно, например, для настройки пользовательских шрифтов и т. Д. Вот метод

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);
        }
    }
}

Вот и все, просто вызовите этот метод после setContentView в своей деятельности. Если вам интересно, какой параметр вы бы передали, это id родительского контейнера. Назначьте id родительскому контейнеру, например

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

и позвони setupUI(findViewById(R.id.parent)), вот и все.

Если вы хотите использовать это эффективно, вы можете создать расширенный Activity и поместить в него этот метод, а также сделать так, чтобы все другие действия в вашем приложении расширяли это действие и вызывали его setupUI() в методе onCreate().

Надеюсь, это поможет.

Если вы используете более одного действия, определите общий идентификатор для родительского макета, например <RelativeLayout android:id="@+id/main_parent"> ... </RelativeLayout>

Затем расширьте класс из Activity и определите setupUI(findViewById(R.id.main_parent)) в его OnResume() и расширьте этот класс вместо Activity in your program


Вот версия Kotlin вышеуказанной функции:

@file:JvmName("KeyboardUtils")

fun Activity.hideSoftKeyboard() {
    currentFocus?.let {
        val inputMethodManager = ContextCompat.getSystemService(this, InputMethodManager::class.java)!!
        inputMethodManager.hideSoftInputFromWindow(it.windowToken, 0)
    }
}
person Navneeth G    schedule 25.07.2012
comment
Я не тестировал себя, но похоже, что это сработает, и, поскольку у него высокие отзывы, я изменю принятый ответ на это. - person htafoya; 21.03.2013
comment
Это отличное решение, но в нем есть недостаток, по крайней мере, для меня. Когда я снова касаюсь EditText после его закрытия (нажатие вне EditText), курсор сбрасывается в предыдущую позицию (не там, где я коснулся). Есть идеи, как решить эту мини-ошибку? - person user1709805; 24.05.2013
comment
Он работает gr8, клавиатура исчезает в любом месте приложения, но проблема, с которой я сталкиваюсь, заключается в том, что фокус все еще остается на EditTextView, хотя клавиатура отклонена. Любое решение этой проблемы? - person Javal Nanda; 25.08.2013
comment
Не должно быть очень сложно? Я не занимаюсь программированием для Android прямо сейчас, поправьте меня, если я ошибаюсь. Вы могли бы каким-то образом отслеживать сфокусированный EditText в любой момент и просто попросить его потерять фокус во время OnTouchEvent? - person Navneeth G; 26.08.2013
comment
Не уверен, что кто-то еще сталкивался с этой проблемой, но это приводит к сбою приложения при вызове hideSoftKeyboard, если ничего не сфокусировано. Вы можете решить эту проблему, заключив вторую строку метода в if(activity.getCurrentFocus() != null) {...} - person Frank Cangialosi; 02.11.2013
comment
Я думаю hideSoftKeyboard (); должно быть hideSoftKeyboard (YourActivity.this); в публичном логическом onTouch. В любом случае этот фрагмент мне очень помог !!!!! Спасибо! - person Theodoros80; 24.02.2014
comment
Проблема с этим подходом заключается в том, что он предполагает, что всем другим представлениям никогда не нужно будет устанавливать для них OnTouchListener. Вы можете просто установить эту логику в ViewGroup.onInterceptTouchEvent(MotionEvent) для корневого представления. - person Alex.F; 03.04.2014
comment
Ответ Vida потрясающий. Проверьте ниже. Полностью работает. - person Darpan; 10.09.2014
comment
Работает отлично. Но вам может потребоваться добавить тест, чтобы избежать потенциального NPE, когда getCurrentFocus () может вернуть null, если ни одно поле не имеет фокус при начальной загрузке формы. если (activity.getCurrentFocus ()! = null) {inputMethodManager.hideSoftInputFromWindow (activity.getCurrentFocus (). getWindowToken (), 0); } - person angryITguy; 24.09.2015
comment
Спасибо за отличное решение! Мы использовали его, но обнаружили, что иногда элементы пользовательского интерфейса не получают события касания, если закрытие клавиатуры перемещает их, поэтому мы заключили вызов hideSoftKeyboard в v.post(new Runnable() { public void run() { hideSoftKeyboard(MyActivity.this); } }); - person Lenny T; 29.01.2016
comment
Поскольку события касания поднимаются вверх по иерархии представлений, я просто добавил их в свой корневой RelativeLayout. - person David; 15.04.2016
comment
События всплывают, нет причин устанавливать слушателя для каждого ребенка. Просто установите его в корень, и готово. - person Lorenz03Tx; 26.04.2016
comment
Не работает, когда я нажимаю другие элементы управления, не закрывая клавиатуру - person mohitum; 05.05.2016
comment
Он хорошо работает до Android Naught. Есть одна проблема, фокус редактирования текста все еще остается, даже если клавиатура скрыта - person Nikhil; 02.06.2017
comment
Он создает новый OnTouchListener для каждого представления. Если имеется 100 представлений, вы создадите 100 экземпляров одного и того же OnTouchListener. Разве не лучше просто создать один OnTouchListener и установить их для всех представлений? Я сделал это, и это сработало так же. - person Damn Vegetables; 15.06.2018
comment
Что тут происходит?? Я действительно надеюсь, что люди, ответственные за (в настоящее время) 518 голосов за, найдут время, чтобы исправить свои 518 приложений, чтобы вместо этого использовать View.OnFocusChangeListener. - person Stephan Henningsen; 23.10.2018
comment
stackoverflow.com/a/54308582/2064565 Этот ответ сработал для меня. - person Neeraj; 09.08.2019
comment
функция принимает активность в качестве входных данных, что, если мне нужно вызвать ее из диалогового окна? - person Dinu Nicolae; 01.04.2020

Вы можете добиться этого, выполнив следующие действия:

  1. Сделайте родительский вид (представление содержимого вашей активности) интерактивным и настраиваемым, добавив следующие атрибуты

        android:clickable="true" 
        android:focusableInTouchMode="true" 
    
  2. Реализуйте метод hideKeyboard ()

        public void hideKeyboard(View view) {
            InputMethodManager inputMethodManager =(InputMethodManager)getSystemService(Activity.INPUT_METHOD_SERVICE);
            inputMethodManager.hideSoftInputFromWindow(view.getWindowToken(), 0);
        }
    
  3. Наконец, установите onFocusChangeListener вашего edittext.

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

Как указано в одном из комментариев ниже, это может не сработать, если родительским представлением является ScrollView. В таком случае интерактивный и фокусируемый режим InTouchMode может быть добавлен в представление непосредственно под ScrollView.

person vida    schedule 07.11.2013
comment
На мой взгляд, это правильный ответ. Меньше кода, никаких ненужных итераций ... - person gattshjoty; 25.02.2014
comment
Мне очень нравится этот ответ. Следует отметить, что это не сработало для меня при добавлении clickable и focusableInTouchMode в мой корневой элемент ScrollView. Мне пришлось добавить к прямому родительскому элементу моего EditText, который был LinearLayout. - person Adam Johns; 29.08.2014
comment
@AdamJohns +1 Протестировал этот сценарий, и вы правы. Я думаю, причина в том, что ScrollView также обрабатывает событие касания. Я добавлю это как примечание к ответу. - person vida; 11.09.2014
comment
У меня отлично сработало. Хотя, если у вас есть два виджета edittext, вам нужно убедиться, что вы правильно обрабатываете onfocus для обоих из них, иначе вы без необходимости будете переключать скрытие клавиатуры. - person Marka A; 24.03.2015
comment
@MarkaA У меня не было проблем с этим, когда я нажимаю на другой EditText, клавиатура остается, если щелкнуть по фону, она скрывается, как и должна. У меня была другая обработка в onFocusChange, просто изменение фона EditText, когда он имеет фокус, ничего особенного. - person CularBytes; 02.07.2015
comment
Отлично работает, спасибо. Гораздо проще и удобнее, чем принятый ответ - person ; 06.12.2015
comment
У меня это хорошо работает в большинстве сценариев. У меня есть 8 редактируемых текстов в моем фрагменте, и я реализовал OnFocusChangeListener для каждого, в котором я отключаю клавиатуру. У меня также есть кнопка сохранения на панели инструментов, и, щелкнув по ней, я снова отключаю клавиатуру. Но при нажатии кнопки «Сохранить» клавиатура скрывается, я вытаскиваю фрагмент из задней стопки, и когда мой предыдущий фрагмент виден, клавиатура снова появляется. Любое решение для этого. Пожалуйста, помогите. - person AndroidGuy; 12.01.2016
comment
@AndroidGuy раньше этого не пробовал. Я постараюсь реализовать и посмотрю, смогу ли я предложить решение. Или, может быть, кто-то другой тоже найдет решение. Если вы тоже можете решить эту проблему, оставьте комментарий или вы можете отредактировать ответ, чтобы добавить его. - person vida; 21.01.2016
comment
У меня не работает. Должен ли вид содержимого иметь фон или что-то еще, чтобы это работало? Мое представление содержимого - android.support.v7.widget.ContentFrameLayout, Android 6.0.1 - person sydd; 14.07.2016
comment
Проголосовали за, но это нормально, только если у вас не слишком много EditTexts и макет прост. - person Fundhor; 13.01.2017
comment
Работает, но переключение с одного EditText на другой заставит клавиатуру скрываться и всплывать (это заметно при просмотре треугольника или кнопки возврата) - person Nikola; 15.09.2017
comment
попробовал, это решение работает нормально, но если у меня есть несколько редактируемых текстов в действии, я вижу какой-то эффект мерцания, который действительно раздражает. Как мне от этого избавиться? - person Shrikant; 20.12.2017
comment
Это не сработает, если пользователь щелкнет по дочерним элементам, на которых можно сфокусироваться. - person atjua; 04.05.2018
comment
каждый раз, когда мне нужно спрятаться, приходи сюда и снова используй ответ !, так что извините, что мы не можем проголосовать за ваш ответ более одного раза;)) - person MHSFisher; 17.06.2018
comment
мне нравится этот ответ .. вам нужно установить интерактивный и focusableInTouchMode в родительском макете, например ScrollView, LinearLayout, ConstraintLayout - person Rio Adetya Rizky; 22.07.2018
comment
@Shrikant - Я тоже видел мерцание при редактировании нескольких текстов. Я просто установил onFocusChangeListener для родительского элемента, а не для каждого редактируемого текста, и переключил условие, чтобы сказать if (hasFocus) {hideKeyboard (v); } Больше не замечал мерцания при переключении текста редактирования btwn. - person manisha; 06.09.2018
comment
я не уверен, когда я использую только этот android:clickable="true" android:focusableInTouchMode="true" в родительском представлении, тогда программная клавиатура не появляется - person Yohanes AI; 27.09.2020
comment
android: clickable = true испортит режим доступности. Он уведомит пользователя о двойном щелчке мыши для активации в режиме доступности. - person NOT_A_PROGRAMMER; 12.01.2021

Просто переопределите код ниже в 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);
}

ОБНОВЛЕНИЕ:

Если кому-то нужна версия этого ответа на Kotlin:

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)
}
person sumit sonawane    schedule 22.01.2019
comment
Простое решение, добавьте действие, и оно также позаботится о фрагменте - person Rohit Maurya; 22.03.2019
comment
Другое решение - создать BaseActivity и распространить его на все Activity - person sumit sonawane; 28.03.2019
comment
Гениальное решение - person Ahmed Adel Ismail; 03.05.2019
comment
вы спасли меня от мерцания экрана при закрытии клавиатуры. Пальцы вверх! - person Dimas Mendes; 29.06.2019
comment
Хотя это красивое и чистое решение, если у нас есть ввод внутри прокручиваемого представления, он не будет прокручиваться, когда клавиатура открыта, а вместо этого отключает клавиатуру. - person angelos_lex; 15.04.2020
comment
Красиво, но это было слишком хорошо, чтобы быть правдой: просто, очень коротко, и это сработало ... увы, есть проблема: когда отображается клавиатура, каждый раз, когда мы касаемся EditText, который запрашивал клавиатуру, он опускается и вверх автоматически. - person Chrysotribax; 22.04.2020
comment
это, к сожалению, скроет клавиатуру в событии TOUCH, поэтому, даже если вы просто хотите прокрутить или сделать ошибочное касание, она скроет - person TootsieRockNRoll; 14.05.2020
comment
Замечательный ответ !!! - person Sachin K Pissay; 19.02.2021
comment
вау сработало для меня, спасибо приятель. - person Abdul Basit Rishi; 15.06.2021

Я считаю принятый ответ немного сложным.

Вот мое решение. Добавьте OnTouchListener в свой основной макет, то есть:

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

и поместите следующий код в метод onTouch.

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

Таким образом, вам не нужно перебирать все представления.

person roepit    schedule 10.09.2013
comment
@roepit - я получаю classCastexception за попытку приведения макета в представление. я что-то упускаю? - person katzenhut; 24.02.2014
comment
Можете ли вы где-нибудь сослаться на свой код? Я не могу сказать, что не так, если не могу посмотреть на ваш макет и код активности / фрагмента и так далее. - person roepit; 24.02.2014
comment
Лучший ответ там, все еще пытаюсь понять, как это работает. - person Pol; 25.05.2016
comment
будет ли это работать, если щелкнуть строку заголовка приложения? - person user1506104; 26.04.2017
comment
Это отлично работает для скрытия клавиатуры! Обратите внимание, что на самом деле это не расфокусирует EditText, а просто скрывает клавиатуру. Чтобы также расфокусировать EditText, добавьте, например, android:onClick="stealFocusFromEditTexts" в xml родительского представления, а затем public void stealFocusFromEditTexts(View view) {} в его активность. Метод по щелчку ничего не должен делать, он просто должен существовать, чтобы родительский вид мог фокусироваться / выбираться, что необходимо для кражи фокуса у дочернего EditText. - person Jacob R; 31.08.2017

У меня есть еще одно решение, чтобы скрыть клавиатуру:

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

Здесь передайте HIDE_IMPLICIT_ONLY в позиции showFlag и 0 в позиции hiddenFlag. Это принудительно закроет мягкую клавиатуру.

person Saurabh Pareek    schedule 03.01.2012
comment
Спасибо, это работает ... прежде всего, я пробовал, но он не работает, пока я получаю значение из диалогового окна editext text и закрытия диалогового окна ... - person PankajAndroid; 29.07.2013
comment
Спасибо, это работает только и намного чище, чем предыдущие! +1 - person Edmond Tamas; 10.12.2014
comment
Он работает, как ожидалось, спасибо за ваше решение +1 - person tryp; 11.01.2016
comment
Для меня работает как шарм. +1 за элегантное решение. - person saintjab; 15.01.2016
comment
извините, но этот метод переключаемый, поэтому, если состояние клавиатуры уже закрыто, будет отображаться клавиатура - person HendraWD; 18.10.2016

Что ж, мне удалось несколько решить проблему, я переопределил dispatchTouchEvent в своей деятельности, там я использую следующее, чтобы скрыть клавиатуру.

 /**
 * 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);
}

РЕДАКТИРОВАТЬ: Метод getFields () - это просто метод, который возвращает массив с текстовыми полями в представлении. Чтобы избежать создания этого массива при каждом прикосновении, я создал статический массив с именем sFields, который возвращается в методе getFields (). Этот массив инициализируется такими методами onStart (), как:

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


Это не идеально. Время события перетаскивания основано только на эвристике, поэтому иногда оно не скрывается при выполнении длинных кликов, и я также закончил, создав метод для получения всех editTexts для каждого представления; иначе клавиатура скроется и отобразится при нажатии другого EditText.

Тем не менее, приветствуются более чистые и короткие решения.

person htafoya    schedule 12.11.2010
comment
Чтобы помочь другим в будущем, не могли бы вы отредактировать код в своем ответе, включив в него свой getFields() метод? Он не обязательно должен быть точным, это просто пример с некоторыми комментариями, указывающими на то, что он возвращает массив из EditText объектов. - person Squonk; 15.10.2011

Еще один способ Kotlin и Material Design с использованием TextInputEditText (этот подход также совместим с EditTextView) ...

1. Сделайте родительский вид (представление содержимого вашей активности / фрагмента) интерактивным и настраиваемым, добавив следующие атрибуты

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

2. Создайте расширение для всех View (например, внутри файла ViewExtension.kt):

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

3. Создайте BaseTextInputEditText, унаследованный от TextInputEditText. Реализуйте метод onFocusChanged, чтобы скрыть клавиатуру, когда представление не сфокусировано:

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.Просто назовите свое новое пользовательское представление в своем 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> 

Это все. Нет необходимости изменять контроллеры (фрагмент или действие) для обработки этого повторяющегося случая.

person Phil    schedule 01.05.2018

Используйте OnFocusChangeListener.

Например:

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

Обновление: вы также можете переопределить onTouchEvent() в своей деятельности и проверить координаты касания. Если координаты находятся за пределами EditText, скройте клавиатуру.

person Sergey Glotov    schedule 12.11.2010
comment
Проблема в том, что редактируемый текст не теряет фокус, когда я нажимаю на ярлык или другие представления, на которые нельзя сфокусироваться. - person htafoya; 12.11.2010
comment
В этом случае у меня есть еще одно решение. Я обновил ответ. - person Sergey Glotov; 12.11.2010
comment
onTouchEvent вызывается много раз, так что это тоже не очень хорошая практика. - person Jesus Dimrix; 14.08.2016

Я реализовал dispatchTouchEvent в Activity, чтобы сделать это:

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);
}

и я его протестировал, работает отлично!

person Jishi Chen    schedule 24.05.2014
comment
работает, но проблема в том, что если у нас более одного EditText, нам тоже нужно это учитывать, но мне понравился ваш ответ :-) - person Lalit Poptani; 18.06.2014
comment
getActionMasked (ev) устарел, поэтому теперь используйте: final int action = ev.getActionMasked (); для первой строки. - person Andrew; 04.01.2019

Переопределить публичное логическое событие dispatchTouchEvent (событие MotionEvent) в любом действии (или расширить класс действия)

@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;
}

Версия Котлина:

 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
    }

И это все, что тебе нужно сделать

person Hoang Trinh    schedule 27.07.2014
comment
это был самый простой способ заставить его работать. работает с несколькими EditTexts и ScrollView - person RJH; 07.01.2016
comment
Протестировал с несколькими EditTexts; оно работает! Единственный недостаток в том, что при перетаскивании оно тоже скрывается. - person RominaV; 13.03.2016

Я знаю, что эта ветка довольно старая, правильный ответ кажется верным и есть много рабочих решений, но я думаю, что изложенный ниже подход может иметь дополнительное преимущество в отношении эффективности и элегантности.

Мне нужно это поведение для всех моих действий, поэтому я создал класс CustomActivity, наследующий от класса Activity, и "подключил" функцию dispatchTouchEvent. В основном необходимо соблюдать два условия:

  1. Если фокус не изменился и кто-то нажимает за пределами текущего поля ввода, закройте IME
  2. Если фокус изменился и следующий элемент в фокусе не является экземпляром какого-либо поля ввода, отклоните IME.

Это мой результат:

@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);
}

Боковое примечание: Кроме того, я назначаю эти атрибуты корневому представлению, что позволяет очищать фокус для каждого поля ввода и предотвращает фокусировку полей ввода при запуске активности (что делает представление содержимого «ловушкой фокуса»):

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

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

    view.setFocusable(true);
    view.setFocusableInTouchMode(true);
}
person fje    schedule 23.04.2013
comment
Супер, все отлично, спасибо! я поставил один голос за ваш ответ. - person vijay; 07.03.2015
comment
Думаю, это лучшее решение для сложных макетов. Но пока я обнаружил 2 недостатка: 1. Контекстное меню EditText неактивно - любой щелчок по нему приводит к потере фокуса EditText 2. Когда наш EditText находится внизу представления и мы долго щелкаем по нему (чтобы выбрать слово), затем после клавиатура показала, что наша точка щелчка находится на клавиатуре, а не на EditText, поэтому мы снова теряем фокус: / - person wrozwad; 10.07.2015
comment
@sosite, я думаю, что в своем ответе я рассмотрел эти ограничения; взглянем. - person Andy Dennie; 24.09.2015
comment
@sosite Я использую аналогичный код и проблем с контекстным меню нет. События касания в контекстном меню не отправляются в мою активность. - person Kamen Dobrev; 31.12.2020

Я изменил решение Андре Луиса IM, я добился этого:

Я создал служебный метод, чтобы скрыть мягкую клавиатуру так же, как это сделал Андре Луис IM:

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

Но вместо того, чтобы регистрировать OnTouchListener для каждого представления, которое дает низкую производительность, я зарегистрировал OnTouchListener только для корневого представления. Поскольку событие пузырится до тех пор, пока оно не будет использовано (EditText - одно из представлений, которое потребляет его по умолчанию), если оно попадает в корневое представление, это потому, что оно не использовалось, поэтому я закрываю программную клавиатуру.

findViewById(android.R.id.content).setOnTouchListener(new OnTouchListener() {
    @Override
    public boolean onTouch(View v, MotionEvent event) {
        Utils.hideSoftKeyboard(activity);
        return false;
    }
});
person Fernando Camargo    schedule 12.10.2012
comment
Мне так кажется безопаснее. - person superarts.org; 30.07.2014
comment
Это и ответы @AndyMc - именно то, что мне нужно. - person Jax Teller; 18.12.2020

Вместо перебора всех представлений или переопределения dispatchTouchEvent.

Почему бы просто не переопределить onUserInteraction () Activity, это обеспечит отключение клавиатуры всякий раз, когда пользователь нажимает за пределами EditText.

Будет работать, даже если EditText находится внутри scrollView.

@Override
public void onUserInteraction() {
    if (getCurrentFocus() != null) {
        InputMethodManager imm = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE);
        imm.hideSoftInputFromWindow(getCurrentFocus().getWindowToken(), 0);
    }
}
person SKG    schedule 14.05.2020
comment
это лучший ответ - person ovluca; 23.05.2020
comment
Да! Это супер чистый ответ. И если вы создаете AbstractActivity, который расширяют все ваши другие Activity, вы можете запечь это как поведение по умолчанию для всего вашего приложения. - person wildcat12; 30.05.2020
comment
В котлине if (currentFocus != null && currentFocus !is EditText) дал мне лишнюю милю - person Thinkal VB; 20.05.2021

Мне понравился способ вызова dispatchTouchEvent, сделанный htafoya, но:

  • Я не понял часть таймера (не знаю, зачем нужно измерять время простоя?)
  • Мне не нравится регистрировать / отменять регистрацию всех EditTexts при каждом изменении вида (может быть довольно много изменений вида и edittexts в сложных иерархиях)

Итак, я сделал это несколько более простое решение:

@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());
}

Есть один недостаток:

Переключение с одного EditText на другой EditText заставляет клавиатуру скрывать и повторно отображать - в моем случае это желательно, потому что это показывает, что вы переключились между двумя входными компонентами.

person Christian R.    schedule 19.03.2013
comment
Этот метод работал лучше всего с точки зрения возможности просто подключать и работать с моим FragmentActivity. - person BillyRayCyrus; 16.01.2015
comment
Спасибо, это лучший способ! Я только что добавил проверку на действие события: int action = ev.getActionMasked (); if (действие == MotionEvent.ACTION_DOWN || действие == MotionEvent.ACTION_POINTER_DOWN) {...} - person sergey.n; 28.09.2015
comment
Спасибо ! Вы сэкономили мне время .. Лучший ответ. - person I.d007; 17.12.2018

Мольба: я понимаю, что не имею никакого влияния, но, пожалуйста, отнеситесь к моему ответу серьезно.

Проблема: отключите программную клавиатуру при нажатии вне клавиатуры или редактируйте текст с минимальным кодом.

Решение. Внешняя библиотека, известная как Butterknife.

Однострочное решение:

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

Более понятное решение:

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

Объяснение: привяжите OnClick Listener к родительскому идентификатору макета XML действия, чтобы любой щелчок по макету (не по редактируемому тексту или клавиатуре) запускал этот фрагмент кода, который скрывал клавиатуру.

Пример. Если ваш файл макета - R.layout.my_layout, а ваш идентификатор макета - R.id.my_layout_id, то ваш вызов привязки Butterknife должен выглядеть так:

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

Ссылка на документацию Butterknife: http://jakewharton.github.io/butterknife/

Plug: Butterknife произведет революцию в разработке приложений для Android. Обдумай это.

Примечание: того же результата можно достичь без использования внешней библиотеки Butterknife. Просто установите OnClickListener для родительского макета, как описано выше.

person Charles Woodson    schedule 05.04.2017
comment
Замечательное, отличное решение! Спасибо. - person nullforlife; 30.05.2017

В котлине мы можем сделать следующее. Нет необходимости повторять все просмотры. Это также будет работать для фрагментов.

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)
}
person Sai    schedule 02.07.2019
comment
это работает, но в нем есть ошибка. например, если я хочу вставить текст в текстовое окно, клавиатура скрывается, а затем отображается. Это немного раздражает. - person George Shalvashvili; 23.01.2020

Вот еще один вариант ответа fje, который касается вопросов, поднятых сайтом.

Идея здесь состоит в том, чтобы обрабатывать действия как вниз, так и вверх в методе dispatchTouchEvent Activity. При действии вниз мы обращаем внимание на текущий сфокусированный вид (если таковой имеется) и на то, было ли касание внутри него, сохраняя обе эти части информации на будущее.

При действии up мы сначала отправляем, чтобы позволить другому представлению потенциально сфокусироваться. Если после этого текущий сфокусированный вид является первоначально сфокусированным видом, а касание вниз было внутри этого вида, то мы оставляем клавиатуру открытой.

Если текущее сфокусированное представление отличается от первоначально сфокусированного вида и это EditText, то мы также оставляем клавиатуру открытой.

В противном случае закрываем.

Итак, вкратце, это работает следующим образом:

  • при прикосновении к EditText, находящемуся в фокусе, клавиатура остается открытой
  • при переходе от сфокусированного EditText к другому EditText клавиатура остается открытой (не закрывается / не открывается)
  • при прикосновении к чему-либо за пределами текущего EditText, который не является другим EditText, клавиатура закрывается
  • при длительном нажатии на EditText для вызова контекстной панели действий (с кнопками вырезания / копирования / вставки) клавиатура остается открытой, даже если действие ВВЕРХ произошло за пределами сфокусированного EditText (который переместился вниз, чтобы освободить место для ТАКСИ). Однако обратите внимание, что когда вы нажимаете кнопку в CAB, клавиатура закрывается. Это может быть желательным, а может и нежелательным; если вы хотите вырезать / скопировать из одного поля и вставить в другое, это будет. Если захочешь вставить обратно в тот же EditText, этого бы не было.
  • когда сфокусированный EditText находится в нижней части экрана и вы долгое время щелкаете по тексту, чтобы выделить его, EditText сохраняет фокус, и поэтому клавиатура открывается так, как вы хотите, потому что мы выполняем проверку «касание находится в пределах границ обзора» для действие вниз, а не действие вверх.

    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);
    }
    
person Andy Dennie    schedule 24.09.2015

это слишком просто, просто сделайте свой недавний макет интерактивным и фокусируемым с помощью этого кода:

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

а затем напишите метод и OnClickListner для этого макета, чтобы при касании верхнего макета в любом месте он вызывал метод, в котором вы напишете код для отключения клавиатуры. Ниже приведен код для обоих; // вы должны записать это в OnCreate ()

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

метод, вызываемый из списка: -

 public void hideKeyboard(View view) {
     InputMethodManager imm =(InputMethodManager)getSystemService(Activity.INPUT_METHOD_SERVICE);
        imm.hideSoftInputFromWindow(view.getWindowToken(), 0);
    }
person swarnim dixit    schedule 20.10.2016

Я считаю принятый ответ немного сложным для этого простого требования. Вот что у меня сработало без сбоев.

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;
        }
    });
person Joohay    schedule 20.05.2018
comment
Если вы используете NestedScrollView или сложные макеты, см. Принятый ответ: stackoverflow.com/a/11656129/2914140. Вы должны знать, что другие контейнеры могут поглощать прикосновения. - person CoolMind; 04.02.2019

Это самое простое решение для меня (и оно разработано мной).

Это способ скрыть клавиатуру.

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

Теперь установите атрибут onclick родительского макета действия на указанный выше метод hideKeyboard либо из представления «Дизайн» вашего XML-файла, либо с написанием кода ниже в текстовом представлении вашего XML-файла.

android:onClick="hideKeyboard"
person NullByte08    schedule 16.06.2019

Существует более простой подход, основанный на той же проблеме с iPhone. Просто переопределите макет фона при событии касания, где содержится текст редактирования. Просто используйте этот код в OnCreate активности (login_fondo - это корневой макет):

    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;
                }
            });
person Alex R. R.    schedule 12.07.2012
comment
Как я сказал и помню, это работает, только если форма не находится внутри ScrollView. - person htafoya; 13.07.2012
comment
Это не очень хорошо работает, если фоновый макет содержит другие дочерние макеты. - person AxeEffect; 12.03.2013
comment
Спасибо, что напомнили, что onClick - не единственный вариант. - person Harpreet; 11.11.2019

Метод отображения / скрытия мягкой клавиатуры

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);    
        }

    }

Я надеюсь они были полезны

person lalosoft    schedule 10.08.2012

Я получил это, работая с небольшим вариантом решения Фернандо Камараго. В моем методе onCreate я прикрепляю один onTouchListener к корневому представлению, но отправляю представление, а не действие в качестве аргумента.

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

В отдельном классе Utils есть ...

    public static void hideSoftKeyboard(View v) {
    InputMethodManager imm = (InputMethodManager) v.getContext().getSystemService(Context.INPUT_METHOD_SERVICE); 
    imm.hideSoftInputFromWindow(v.getWindowToken(), 0);
}
person AndyMc    schedule 04.03.2013

Я сделал так:

@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);
}

Скрыть код клавиатуры:

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

Готово

person Hiren Patel    schedule 01.03.2016

Чтобы решить эту проблему, вам нужно сначала использовать setOnFocusChangeListener этого 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");
                }
            }
        });

а затем вам нужно переопределить dispatchTouchEvent в действии, которое содержит этот Edittext, см. ниже код

@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);
}

Теперь, что произойдет, когда пользователь щелкнет снаружи, тогда сначала будет вызвано это dispatchTouchEvent, которое затем очистит фокус от редактируемого текста, теперь ваш OnFocusChangeListener будет вызываться, этот фокус был изменен, теперь здесь вы можете делать все, что хотели, надеюсь, что это сработает

person Sudhanshu Gaur    schedule 04.04.2016

Я усовершенствовал метод, поместив следующий код в некоторый служебный класс пользовательского интерфейса (желательно, не обязательно), чтобы к нему можно было получить доступ из всех ваших классов Activity или Fragment для выполнения своей цели.

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);
}

Затем скажите, например, что вам нужно вызвать его из активности, вызовите его следующим образом;

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

Уведомление

findViewById (android.R.id.content)

Это дает нам корневое представление текущей группы (вы не должны устанавливать идентификатор в корневом представлении).

Ваше здоровье :)

person Uzair    schedule 22.04.2016

Попробуйте включить stateHidden в качестве значения windowSoftInputMode активности

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

Например, для вашей деятельности:

this.getWindow().setSoftInputMode(
    WindowManager.LayoutParams.SOFT_INPUT_STATE_HIDDEN);
person Alex Volovoy    schedule 12.11.2010

Деятельность

 @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);
 }
person icebail    schedule 01.11.2017
comment
Этот код прост, но имеет очевидную проблему: он закрывает клавиатуру при касании в любом месте. То есть, если вы коснетесь другого места EditText, чтобы переместить курсор ввода, он скроет клавиатуру, и она снова появится в системе. - person Damn Vegetables; 15.06.2018

Просто добавьте этот код в класс @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);
}
person Haseeb Javed    schedule 05.12.2017
comment
Хотя это может дать ответ на вопрос, лучше объяснить основные части ответа и, возможно, в чем проблема с кодом OP. - person pirho; 05.12.2017
comment
да @pirho Я также согласен с вами, Хасиб должен сосредоточиться на том, чтобы дать правильный ответ. - person Dilip; 05.12.2017
comment
@Dilip Знаете ли вы, что вы также можете голосовать за комментарии, с которыми согласны? Это сделано для того, чтобы раздел комментариев оставался чистым, чтобы не было много комментариев, имеющих одну и ту же точку зрения. - person pirho; 05.12.2017

вы можете реализовать View.onClickListener и переопределить метод onClick и установить этот onclicklistener в 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);
        }
    }
person Tarandeepsingh    schedule 28.12.2019

Это может быть старым, но я получил это, реализовав собственный класс

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);
}

Лучшая практика здесь - создать вспомогательный класс, и каждый контейнер Relative / Linear Layouts должен реализовывать это.

**** Обратите внимание, что только основной контейнер должен реализовывать этот класс (для оптимизации) ****

и реализуем это так:

Parent.setOnClickListener( new DismissKeyboardListener(this) ); 

ключевое слово this для Activity. поэтому, если вы находитесь на фрагменте, вы используете как getActivity ();

--- поднимите палец вверх, если это вам поможет ... --- приветствует Ральф ---

person ralphgabb    schedule 17.10.2014

Это немного измененная версия ответа fje, которая в основном работала отлично.

В этой версии используется ACTION_DOWN, поэтому при выполнении действия прокрутки также закрывается клавиатура. Он также не распространяет событие, пока вы не нажмете на другой EditText. Это означает, что щелчок в любом месте за пределами вашего EditText, даже на другом кликабельном, просто закрывает клавиатуру.

@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);
}
person Cadmonkey33    schedule 14.05.2015
comment
Что-то здесь не так; вы присваиваете view и viewTmp getCurrentFocus(), поэтому они всегда будут иметь одно и то же значение. - person Andy Dennie; 24.09.2015

Что ж, вы можете использовать этот код, используйте свой идентификатор основного макета вместо 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);
        }

    });
person vinod wakade    schedule 16.07.2017

Я немного изменил ответ @ navneeth-g, добавив обработку фокуса нулевой активности, избегая создания нескольких экземпляров OnTouchListener, убрав фокус, когда клавиатура скрывается, и удалил скрытие клавиатуры при прокрутке экрана, что удобно на небольших устройствах.

//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"
person Zakhar Rodionov    schedule 01.06.2020
comment
Спасибо ! это поможет мне после попытки многих методов. - person Kuldeep mourya; 02.12.2020

Другая идея - переопределить метод onInterceptTouchEvent в корневом представлении для вашей деятельности.

Событие касания идет от самого переднего вида на экране (где произошло событие касания) вниз по стеку представлений, вызывающих метод onTouch, до тех пор, пока какое-либо из представлений не вернет истину, указывая на то, что событие касания было использовано. Поскольку многие представления по умолчанию используют событие касания (например, EditText или TextView), событие не попадает в корневой метод onTouch Activity View.

Но, прежде чем сделать этот обход, событие касания проходит другой путь, идя от корневого представления вниз по дереву представлений, пока не дойдет до самого переднего представления. Этот обход выполняется вызовом onInterceptTouchEvent. Если метод возвращает истину, он перехватывает событие ... нет, но это небольшая уловка, я не думаю, что вы захотите это сделать или узнать подробности. Что вам нужно знать, так это то, что вы можете переопределить этот метод в корневом представлении для своей Activity и поместить туда код, чтобы скрыть клавиатуру, когда это необходимо.

person Andre Luis IM    schedule 12.11.2010
comment
Я подумал об этом пару часов назад, но мой вопрос в том, как узнать, являются ли координаты события EditText или клавиатурой. Другими словами, как получить представление с учетом координат, поскольку я просто хочу использовать instanceof EditText, я пытаюсь уклониться от реализации этого метода для каждого класса и сравнения границ конкретных объектов. - person htafoya; 12.11.2010
comment
В любом случае, мне понравился твой вопрос. У меня может возникнуть такая же проблема прямо сейчас в проекте, над которым я работаю, и этот вопрос породил несколько хороших идей. Если у меня будет лучшее решение, когда я перейду к проблеме, я опубликую здесь. - person Andre Luis IM; 14.11.2010

Вы можете легко переопределить событие onKey () в активности и фрагментах, чтобы скрыть клавиатуру.

@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;
}
person Ajith Memana    schedule 13.02.2014

Привет, ребята, у меня есть простое решение этой проблемы, и это решение можно использовать для простой регистрации или формы входа в систему. мое решение такое же, как я реализовал в ios setontouch listener to Main view

activity_main.xml добавьте идентификатор в основной относительный макет android:id="@+id/mainlayout"

и добавьте этот код в свою деятельность

  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;
            }
        });
person Swap-IOS-Android    schedule 28.02.2014
comment
Вы можете использовать View mainLayout ... и иметь возможность повторно использовать его независимо от вашего базового типа макета. Однако ваш метод по-прежнему неэффективен, если пользователь нажимает на представление, которое потребляет события касания, такие как кнопки и счетчики. - person William T. Mallard; 03.08.2014

Думал это проблема. Во-первых, я думаю, что setOnTouchListener - непростое решение. поэтому я считаю, что dispatchTouchEvent - лучшее простое решение.

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);
}

здесь важен ACTION_UP.

Я предположил, что EditText показывает только мягкую клавиатуру, иначе не отображает клавиатуру. Тестировал на Android5.0.1 (G3.cat6 от LG).

если вам нужна проверка перетаскивания, нажмите и удерживайте, ..., покажите комментарии выше.

person Hogun    schedule 22.04.2015

Мое решение скрывает клавиатуру при внешнем нажатии в любом действии со всеми текстами редактирования. Не указывая их по очереди.

Сначала добавьте в корневой вид макета xml: android: clickable = "true" android: focusableInTouchMode = "true"

Затем создайте один родительский Acitvity для всех действий, которые вы хотите скрыть от клавиатуры, и укажите метод onResume ():

 @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);
                }
            }
        });
    }

Расширьте свою активность с помощью этого общего действия (полномочия наследования!), И все, каждый раз, когда любой EditText (в любом расширенном действии) теряет фокус, клавиатура будет скрыта.

P.S. hideKeyboard метод:

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

context.getCurrentFocus () не требует указания конкретного представления EditText.

person Misha Akopov    schedule 24.01.2016

Вы можете попробовать способ ниже, у меня он отлично работает :)

Этот способ можно применить для Activity или Fragment, а также он совместим с ScrollView.

Мы помещаем ScrollView в качестве макета верхнего уровня, объявляем id parentView для LinearLayout внутри и добавляем два атрибута, как показано ниже:

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

В коде напишите функцию, как показано ниже:

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

Затем зарегистрируйте OnFocusChangeListener для корневого представления (напишите в методе onCreate), чтобы затронуть весь EditText в Activity:

parentLayout.setOnFocusChangeListener(new View.OnFocusChangeListener() {
            @Override
            public void onFocusChange(View v, boolean hasFocus) {
                if (hasFocus) {
                    hideSoftKeyboard(your_activity_name.this);
                }
            }
        });
person Dang Nguyen    schedule 10.04.2016

Мне удалось спрятать клавиатуру изнутри 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
}
person tony gil    schedule 30.12.2016

В дополнение к предыдущему ответу @sumit sonawane это решение не будет скрывать клавиатуру, если пользователь прокручивает.

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);
}

Добавьте код в свою деятельность, также позаботится о фрагментах.

person JCutting8    schedule 24.04.2020

setupUI((RelativeLayout) findViewById(R.id.activity_logsign_up_RelativeLayout));

Передайте метод в файл макета. Вы должны выбрать общий файл макета в формате XML. Потому что скрытие клавиатуры работает со всей раскладкой.

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);
            }
        }
    }
person Gowtham Subramaniam    schedule 25.11.2016
comment
такой же, как принятый ответ, но хуже, так как вы ссылаетесь на определенный класс LOGSignUpActivity.this, который заставляет код работать только для этого действия - person Gustavo Baiocchi Costa; 08.03.2017

person    schedule
comment
это сделало бы это наверняка - person Jeffy; 03.11.2016
comment
это точно не годится. Это отклоняет клавиатуру, но не отклоняет касание за пределами edittext, как предложенный вопрос! - person Gustavo Baiocchi Costa; 08.03.2017
comment
Почему нет? я сделал тот же подход. работает отлично. в своей версии я поставил в dispatchTouchEvent(). - person user1506104; 26.04.2017