Как скрыть мягкую клавиатуру на андроиде после нажатия вне EditText? - PullRequest
317 голосов
/ 12 ноября 2010

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

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

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

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

IПопытка реализовать прослушивание касанием, щелчком, фокусировкой безуспешно.

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

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

Ответы [ 40 ]

8 голосов
/ 01 мая 2018

A more 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> 

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

7 голосов
/ 13 октября 2012

Я изменил решение Andre Luis 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;
    }
});
6 голосов
/ 20 марта 2013

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

5 голосов
/ 05 апреля 2017

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

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

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

One Line Solution:

@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-макета действия, чтобы любой щелчок по макету (не по тексту редактирования или клавиатуре) запустил этот фрагмент кода, который скроет клавиатуру.1028 * Пример: Если файл макета - 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 Documentation Link: http://jakewharton.github.io/butterknife/

Штекер: Butterknife произведет революцию в развитии Android.Учтите это.

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

4 голосов
/ 20 мая 2018

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

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;
        }
    });
4 голосов
/ 20 октября 2016

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

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);
    }
4 голосов
/ 24 сентября 2015

Вот еще один вариант ответа 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);
    }
    
3 голосов
/ 10 августа 2012

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

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

    }

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

2 голосов
/ 22 апреля 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)

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

Приветствия:)

2 голосов
/ 12 июля 2012

Существует более простой подход, основанный на той же проблеме 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;
                }
            });
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...