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

354

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

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

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

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

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

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

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

htafoya
источник
Ну, я понял, что на самом деле проблема не в скролле, а в ярлыках. Представление представляет собой вертикальный макет с чем-то вроде: TextView, EditText, TextView, EditText и т. Д., И textViews не позволят тексту редактирования потерять фокус и спрятать клавиатуру
htafoya
Вы можете найти решение для getFields()здесь: stackoverflow.com/questions/7790487/…
Reto
Клавиатуру можно закрыть нажатием кнопки возврата, так что я бы сказал, что сомнительно, стоит ли это усилий
gerrytan
4
Я нашел этот ответ: stackoverflow.com/a/28939113/2610855 Лучший.
Loenix

Ответы:

575

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

public static void hideSoftKeyboard(Activity activity) {
    InputMethodManager inputMethodManager = 
        (InputMethodManager) activity.getSystemService(
            Activity.INPUT_METHOD_SERVICE);
    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()методе.

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

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

Затем расширяйте класс из Activityи определяйте setupUI(findViewById(R.id.main_parent))внутри него OnResume()и расширяйте этот класс вместо `` Activityin your program


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

@file:JvmName("KeyboardUtils")

fun Activity.hideSoftKeyboard() {
    currentFocus?.let {
        val inputMethodManager = ContextCompat.getSystemService(this, InputMethodManager::class.java)!!
        inputMethodManager.hideSoftInputFromWindow(it.windowToken, 0)
    }
}
Navneeth G
источник
Я не проверял себя, но похоже, что это сработает, и, поскольку у него высокие отзывы, я поменяю принятый ответ на это.
Хтафоя
4
Не должно быть очень сложно? У меня сейчас нет программирования на Android, так что поправьте меня, если я ошибаюсь. Вы могли бы как-нибудь отследить сфокусированный EditText в любой момент и просто попросить его потерять фокус во время OnTouchEvent?
Навнит Г
25
Не уверен, что кто-то еще сталкивался с этой проблемой, но это вызывает сбой приложения, когда вы вызываете hideSoftKeyboard, если ничего не сфокусировано. Вы можете решить эту проблему, окружив вторую строку метода с помощьюif(activity.getCurrentFocus() != null) {...}
Frank Cangialosi
14
Проблема с этим подходом состоит в том, что он предполагает, что все другие представления никогда не должны будут устанавливать OnTouchListenerих. Вы можете просто установить эту логику в ViewGroup.onInterceptTouchEvent(MotionEvent)корневое представление.
Alex.F
2
Не работает, когда я нажимаю на другие элементы управления, удерживая клавиатуру открытой
mohitum
285

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

  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.setOnFocusChangeListener(new View.OnFocusChangeListener() {
            @Override
            public void onFocusChange(View v, boolean hasFocus) {
                if (!hasFocus) {
                    hideKeyboard(v);
                }
            }
        });

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

Вида
источник
67
На мой взгляд, это правильный ответ. Меньше кода, без лишних итераций ...
gattshjoty
14
Мне очень нравится этот ответ. Стоит отметить, что это не сработало для меня при добавлении clickableи focusableInTouchModeк моему корневому ScrollViewэлементу. Я должен был добавить к прямому родителю моего, EditTextкоторый был LinearLayout.
Адам Джонс
10
Работал отлично для меня. Хотя, если у вас есть два виджета edittext, вам нужно убедиться, что вы правильно обрабатываете их обоих, иначе вы будете без необходимости переключать скрытие клавиатуры.
Марка А
1
@MarkaA У меня не было проблем с этим, когда я нажимаю на другой EditText, клавиатура остается, если щелкнуть по фону, она прячется, как и должна. У меня была другая обработка в onFocusChange, просто изменив фон EditText, когда он имеет фокус, ничего лишнего.
CularBytes
3
@Shrikant - я тоже видел мерцание с несколькими текстами редактирования. Я просто установил onFocusChangeListener для родителя, а не для каждого текста редактирования, и переключил условие, чтобы сказать, если (hasFocus) {hideKeyboard (v); } Больше не замечал мерцания при переключении между текстами редактирования.
Маниша
69

Просто переопределите код ниже в 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);
}
Sumit Sonawane
источник
4
Простое решение, добавьте в действие, и он также позаботится об фрагменте
Рохит Маурья
1
Другое решение заключается в создании BaseActivity и расширении его во всех
видах
1
Гениальное решение
Ахмед Адель Исмаил
1
Вы спасли меня от мерцания экрана при закрытии клавиатуры. Недурно!
Димас Мендес
1
Хорошо, но это было слишком хорошо, чтобы быть правдой: просто, очень коротко, и это работало ... увы, есть проблема: когда отображается клавиатура, каждый раз, когда мы касаемся EditText, который спрашивал клавиатуру, он падает и автоматически.
Chrysotribax
60

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

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

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

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

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

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

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

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

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

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

Саураб Парик
источник
3
Спасибо, это работает ..... прежде всего я пытался, но это не работает, пока я получаю значение из диалогового окна Текст редактора и закрывающее диалоговое окно ...
PankajAndroid
Спасибо, это работает только, и это намного чище, чем другие выше! +1
Эдмонд Тамас
Работает как положено, спасибо за ваше решение +1
tryp
Работает как шарм для меня. +1 за элегантное решение.
сентября
2
извините, но этот метод является переключением, поэтому, если состояние клавиатуры уже закрыто, оно покажет клавиатуру
HendraWD
16

Ну, мне удается несколько решить проблему, я переопределил 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.

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

htafoya
источник
8
Чтобы помочь другим в будущем, рассмотрите возможность редактирования кода в своем ответе, чтобы включить свой getFields()метод? Он не должен быть точным, просто пример с несколькими комментариями, указывающими, что он возвращает массив EditTextобъектов.
Squonk
14

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

Например:

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

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

Сергей Глотов
источник
9
Проблема в том, что текст редактирования не теряет фокус, когда я нажимаю на Метку или другие виды, которые не могут быть сфокусированы.
Хтафоя
В этом случае у меня есть еще одно решение. Я обновил ответ.
Сергей Глотов
2
onTouchEvent вызывали много раз, так что это тоже не очень хорошая практика
Jesus Dimrix
13

Я реализовал 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);
}

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

Джиши Чен
источник
работает, но проблема в том, что если у нас есть более одного EditText, то мы должны это учитывать, но мне понравился ваш ответ :-)
Лалит Поптани
getActionMasked (ev) устарел, поэтому теперь используйте: final int action = ev.getActionMasked (); для первой строки.
Андрей
12

Более удобный способ 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> 

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

Фил
источник
Да, хорошо, но хотелось бы, чтобы был более простой способ!
devDeejay
11

Переопределить общедоступный логический 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;
}

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

Хоанг Тринь
источник
это был самый простой способ, который я нашел, чтобы заставить его работать. работает с несколькими EditTexts и ScrollView
RJH
Протестировал это с несколькими EditTexts; оно работает! Единственным недостатком является то, что когда вы делаете перетаскивание, оно тоже скрывается.
RominaV
9

Я изменил решение Andre Luis IM. Я достиг этого:

Я создал служебный метод, чтобы скрыть программную клавиатуру так же, как это сделал Andre Luiz 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;
    }
});
Фернандо Камарго
источник
1
Это кажется мне безопаснее.
superarts.org
9

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

Мне нужно это поведение для всех моих действий, поэтому я создал класс 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);
}
FJE
источник
Супер хорошо работает, спасибо! я положил один голос за ваш ответ.
Виджей
2
Я думаю, что это лучшее решение для сложных макетов. Но я обнаружил два недостатка: 1. Контекстное меню EditText не щелкает - любой щелчок по нему вызывает потерю фокуса из EditText 2. Когда наш EditText находится в нижней части представления, и мы долго нажимаем на него (чтобы выбрать слово), затем после клавиатура показала, что наша «точка щелчка» находится на клавиатуре, а не на EditText - поэтому мы снова теряем фокус: /
около
@ напротив, я думаю, что я рассмотрел эти ограничения в своем ответе; взглянуть.
Энди Денни
6

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

  • Я не понял часть таймера (не знаю, почему необходимо измерять время простоя?)
  • Я не люблю регистрировать / отменять регистрацию всех 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делает клавиатуру скрытой и заново отображаемой - в моем случае это желательно, потому что это показывает, что вы переключились между двумя компонентами ввода.

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

Оправдание: Я признаю , у меня нет влияния, но пожалуйста мой ответ серьезно.

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

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

Решение One Line:

@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 для родительского макета, как описано выше.

Чарльз Вудсон
источник
Отличное, идеальное решение! Спасибо.
nullforlife
6

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

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
источник
не работает из фрагмента
Nurseyit Tursunkulov
это работает, но есть ошибка. например, если я хочу вставить текст в текстовое представление, клавиатура скрывается, а затем появляется. Это немного раздражает.
Георгий Шалвашвили
Так скажите мне лучшее решение ..
Саи
4

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

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

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

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

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

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

  • при касании внутри фокусируемой в данный момент EditTextклавиатуры остается открытой
  • при переходе от сфокусированного EditTextк другомуEditText клавиатура остается открытой (не закрывается / не открывается)
  • при прикосновении к чему-либо EditText, находящемуся за пределами текущего фокуса, который не является другимEditText , клавиатура закрывается
  • при длительном нажатии в, EditTextчтобы вызвать контекстную панель действий (с кнопками вырезать / копировать / вставить), клавиатура остается открытой, даже если действие ВВЕРХ имело место вне сфокусированного EditText(который сдвинулся вниз, чтобы освободить место для CAB) , Тем не менее, обратите внимание, что когда вы нажимаете на кнопку в 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);
    }
Энди Денни
источник
4

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

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

Метод вызывается из listner:

 public void hideKeyboard(View view) {
     InputMethodManager imm =(InputMethodManager)getSystemService(Activity.INPUT_METHOD_SERVICE);
        imm.hideSoftInputFromWindow(view.getWindowToken(), 0);
    }
Swarnim Dixit
источник
4

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

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
источник
1
Если вы используете NestedScrollViewили сложные макеты, см. Принятый ответ: stackoverflow.com/a/11656129/2914140 . Вы должны знать, что другие контейнеры могут потреблять прикосновения.
CoolMind
3

Существует более простой подход, основанный на той же проблеме 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;
                }
            });
Алекс РР
источник
1
Как я уже говорил и помню, это работает, только если форма не находится внутри ScrollView.
Хтафоя
1
Это не очень хорошо работает, если фоновый макет содержит другие дочерние макеты.
AxeEffect
Спасибо за напоминание, что onClick был не единственным вариантом.
Harpreet
3

Способ показать / скрыть программную клавиатуру

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

    }

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

lalosoft
источник
3

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

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

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

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

android:onClick="hideKeyboard"
NullByte08
источник
2

Я усовершенствовал метод, поместил следующий код в некоторый служебный класс пользовательского интерфейса (желательно, не обязательно), чтобы к нему можно было получить доступ из всех ваших классов 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)

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

Ура :)

Uzair
источник
2

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

 @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);
 }
icebail
источник
3
Этот код прост, но имеет очевидный вопрос: он закрывает клавиатуру , когда в любом прикосновении. То есть, если вы нажмете другое место EditText, чтобы переместить курсор ввода, он скрывает клавиатуру, и клавиатура снова появляется в системе.
Чертовы овощи
2

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

Вместо перебора всех представлений или переопределения 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);
    }
}
SKG
источник
это лучший ответ
Овлюка
Да! Это супер чистый ответ. И если вы создаете AbstractActivity, который расширяет все ваши другие действия, вы можете включить его в качестве поведения по умолчанию во всем приложении.
wildcat12
1

Я получил это работая с небольшим вариантом решения Фернандо Камараго. В моем методе 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);
}
AndyMc
источник
1

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

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

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

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

и реализовать это так:

Parent.setOnClickListener( new DismissKeyboardListener(this) ); 

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

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

ralphgabb
источник
1

Это слегка измененная версия ответа 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);
}
Cadmonkey33
источник
Что-то здесь не выглядит; вы присваиваете как viewи viewTmpк getCurrentFocus(), так что они всегда будут иметь то же значение.
Энди Денни
1

Я сделал так:

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

Выполнено

Хирен Патель
источник