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

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

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

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

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

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

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

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

Ответы [ 40 ]

546 голосов
/ 25 июля 2012

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

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() и расширьте этот класс вместо `` Activity in your program

254 голосов
/ 07 ноября 2013

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

  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.

55 голосов
/ 10 сентября 2013

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

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

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

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

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

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

37 голосов
/ 03 января 2012

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

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

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

16 голосов
/ 12 ноября 2010

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

EDIT: Метод getFields () - это просто метод, который возвращает массив с текстовыми полями в представлении. Чтобы избежать создания этого массива при каждом касании, я создал статический массив с именем sFields, который возвращается методом getFields (). Этот массив инициализируется с помощью методов onStart (), таких как:

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


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

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

15 голосов
/ 22 января 2019

Просто переопределите код ниже в Деятельности

 @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);
}
13 голосов
/ 12 ноября 2010

Использование OnFocusChangeListener .

Например:

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

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

12 голосов
/ 24 мая 2014

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

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

11 голосов
/ 27 июля 2014

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

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

8 голосов
/ 23 апреля 2013

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

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