Як приховати м'яку клавіатуру на Android після натискання кнопки "EditText"?


354

Добре всі знають, що для приховування клавіатури вам потрібно реалізувати:

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

Але головне тут - як приховати клавіатуру, коли користувач торкається або вибирає будь-яке інше місце, яке не є EditTextабо softKeyboard?

Я намагався використовувати функцію " onTouchEvent()батьків", Activityале це працює лише в тому випадку, якщо користувач торкається поза будь-якого іншого виду і немає перегляду прокрутки.

Я намагався реалізувати слухача натисканням, клацанням, фокусуванням без жодного успіху.

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

Чи є стандартний спосіб зробити це ?? в iPhone це було дуже просто.


Я зрозумів, що прокрутка - це насправді не проблема, а мітки, які там є. Перегляд являє собою вертикальний макет із таким чином: TextView, EditText, TextView, EditText і т. Д. І textViews не дозволить редакційному тексту втратити фокус і приховати клавіатуру
htafoya

Ви можете знайти рішення getFields()тут: stackoverflow.com/questions/7790487/…
Рето

Клавіатуру можна закрити натисканням кнопки повернення, тому я б сказав, що сумнівно, чи варто цього докладати зусиль
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якщо він не зареєструє a 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()а розкрийте цей клас замість `` Діяльністьin your program


Ось варіант Котліна з вищевказаною функцією:

@file:JvmName("KeyboardUtils")

fun Activity.hideSoftKeyboard() {
    currentFocus?.let {
        val inputMethodManager = ContextCompat.getSystemService(this, InputMethodManager::class.java)!!
        inputMethodManager.hideSoftInputFromWindow(it.windowToken, 0)
    }
}

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

4
Не повинно бути дуже важко? Зараз я не програмую Android, тому виправте мене, якщо я помиляюся. Ви могли якось відслідковувати сфокусований EditText в будь-який момент і просто просити його втратити фокус під час OnTouchEvent?
Navneeth G

25
Не впевнений, чи хтось інший не стикався з цією проблемою, але це спричиняє збій програми, коли ви викликаєте hidSoftKeyboard, якщо нічого не зосереджено. Ви можете вирішити це, оточуючи другий рядок методу за допомогоюif(activity.getCurrentFocus() != null) {...}
Френк Кангалосі

14
Проблема такого підходу полягає в тому, що він передбачає, що всі інші погляди ніколи не потребуватимуть їх встановлення OnTouchListener. Ви можете просто встановити цю логіку в ViewGroup.onInterceptTouchEvent(MotionEvent)кореневому поданні.
Alex.F

2
Не працює, коли натискаю інші елементи керування, щоб утримувати клавіатуру відкритою
mohitum

285

Ви можете досягти цього, виконавши такі кроки:

  1. Зробіть батьківський вигляд (перегляд вмісту вашої діяльності) клікабельним та орієнтованим, додавши наступні атрибути

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

        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. У такому випадку на панель перегляду безпосередньо під ScrollView може бути доданий інтерактивний та фокусувальний InTouchMode.


67
На мою думку, це правильна відповідь. Менше коду, без зайвих ітерацій ...
gattshjoty

14
Мені ця відповідь дуже подобається. Варто зазначити, що це не спрацювало для мене при додаванні clickableта focusableInTouchModeдо мого кореневого ScrollViewелемента. Мені довелося додати до прямого батька мого, EditTextякий був а LinearLayout.
Адам Джонс

10
Для мене прекрасно працювали. Хоча, якщо у вас є два віджети edittext, вам потрібно переконатися, що ви обробляєте onfocus обидва з них правильно, інакше ви будете перемикати клавіатуру без потреби.
Marka A

1
@MarkaA У мене з цим не виникло проблем, коли я клацну на інший EditText, клавіатура залишається, якщо клацнути на тлі, вона ховається як слід. У мене було інше обробка в onFocusChange, просто змінюючи фон EditText, коли він фокусується, нічого не є фенцією.
CularBytes

3
@Shrikant - Я також бачив мерехтіння з кількома текстами редагування. Я просто встановив onFocusChangeListener на батьківському, а не кожен текст редагування, і переключив умову, щоб сказати, якщо (hasFocus) {hidKeyboard (v); } Більше не помічав мерехтіння, коли перемикав тексти редагування.
manisha

69

Просто перейдіть нижче коду в розділі Діяльність

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

4
Просте рішення, додайте активності, і він також подбає про фрагмент
Rohit Maurya

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 - я отримую classCastexception за спробу подати макет до подання. я щось пропускаю?
katzenhut

Ви можете десь посилати свій код? Я не можу сказати, що не так, коли я не можу переглянути ваш макет та активність / фрагмент та ін.
roepit

Найкраща відповідь там, все ще намагаюся обернути голову навколо того, як це працює тхо.
користувач40797

це буде працювати, якщо ви натиснете рядок заголовка програми?
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 для елегантного рішення.
saintjab

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

EDIT: Метод getFields () - це лише метод, який повертає масив із текстовими полями у поданні. Щоб уникнути створення цього масиву на кожному дотику, я створив статичний масив під назвою sFields, який повертається методом getFields (). Цей масив ініціалізується на таких методах onStart (), як:

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


Це не ідеально, час події перетягування базується лише на евристиці, тому іноді він не приховується при виконанні довгих кліків, і я також закінчив створенням методу для отримання всіх редакцій для кожного виду; в іншому випадку клавіатура буде ховатися і відображатися при натисканні на інший EditText.

І все ж більш чисті та короткі рішення вітаються


8
Щоб допомогти іншим в майбутньому, чи можете ви відредагувати код у своїй відповіді, щоб включити ваш getFields()метод? Це не повинно бути точним, лише приклад, можливо, лише деякі коментарі, що вказують на те, що він повертає масив EditTextоб'єктів.
Сквонк

14

Використовуйте OnFocusChangeListener .

Наприклад:

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

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


9
Проблема полягає в тому, що редакційний текст не втрачає уваги, коли натискаю на мітку або інші види, які не фокусуються.
хтафоя

У цьому випадку у мене є ще одне рішення. Я оновив відповідь.
Сергій Глотов

2
onTouchEvent закликав багато разів, тому це теж не є гарною практикою
Ісус Дімрікс

13

Для цього я реалізував dispatchTouchEvent:

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

Більш Котлин & 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

Я змінив рішення Андре Луїса І.М. Я досяг цього:

Я створив метод утиліти, щоб приховати м'яку клавіатуру так само, як це зробив Андре Луїз 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);
}

Супер його добре працює спасибі !! я поставив одне опитування за вашу відповідь.
vijay

2
Я думаю, що це найкраще рішення для складних макетів. Але я знайшов поки два недоліки: 1. Контекстне меню EditText не натискається - будь-який клацання на ньому втрачає фокус від EditText 2. Коли наш EditText знаходиться внизу огляду, і ми довго натискаємо на нього (щоб вибрати слово), потім після На клавіатурі показана наша "точка натискання" на клавіатурі, а не на EditText - тому ми знову втрачаємо фокус: /
sosite

@sosite, я думаю, що я вирішив ці обмеження у своїй відповіді; Поглянь.
Енді Денні

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.

Однорядне рішення:

@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 до ідентифікатора батьківського макета 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);
}

Посилання на документацію про ножа: http://jakewharton.github.io/butterknife/

Вилка: Масляний нож призведе до революційного розвитку вашого андроїда. Розглянемо це.

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


Дивовижне, ідеальне рішення! Дякую.
nullforlife

6

У котліні ми можемо зробити наступне. Не потрібно повторювати всі погляди. Він також буде працювати на фрагменти.

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

не працює з фрагмента
Нурсейт Турсункулов

це працює, але в ньому є помилка. Наприклад, якщо я хочу вставити текст у перегляд тексту, клавіатура ховається, а потім відображається. Це трохи дратує.
Джордж Шалвашвілі

Тож скажіть мені найкраще рішення ..
Сай

4

Ось ще один варіант відповіді fje, який стосується питань, порушених сайтом.

Ідея тут полягає в тому, щоб обробляти як знижуючі, так і спадні дії в діяльності dispatchTouchEvent методі . Під час дії вниз ми робимо відмітку про зосереджений на даний момент погляд (якщо такий є) та чи був дотик всередині нього, зберігаючи обидва ці біти інформації для подальшого.

На початку дії ми спочатку відправляємо, щоб дозволити іншому погляду потенційно зосередити увагу. Якщо після цього перегляд, орієнтований на даний момент, є початково зосередженим видом, а натискання вниз було всередині цього виду, тоді ми залишаємо клавіатуру відкритою.

Якщо в даний час зосереджений перегляд відрізняється від початково зосередженого вигляду, і він є EditText, тоді ми також залишаємо клавіатуру відкритою.

Інакше ми його закриваємо.

Отже, підводячи підсумок, це працює так:

  • при дотику до фокусується в даний EditTextчас клавіатура залишається відкритою
  • при переході від зосередженої EditTextдо іншої EditTextклавіатура залишається відкритою (не закривається / повторно відкривається)
  • при дотику до будь-якого місця за межами зосередженого в даний момент, EditTextякий не є іншим EditText, клавіатура закривається
  • при тривалому натисканні клавіші a, EditTextщоб відкрити контекстну панель дій (кнопками вирізати / скопіювати / вставити), клавіатура залишається відкритою, навіть якщо дія UP відбувалася поза зосередженою 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);
                }
            });

метод, викликаний з списку:

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

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

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.
htafoya

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

    }

Я сподіваюся, що вони були корисні


3

Це для мене найпростіше рішення (і розроблене мною).

Це спосіб приховати клавіатуру.

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"

2

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

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

Діяльність

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

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
Хоча це може відповісти на питання, краще пояснити суттєві частини відповіді та, можливо, у чому була проблема з кодом ОП.
пірхо

так @pirho Я також згоден з вами. Хайзебу потрібно зосередитися на наданні правильної відповіді.
Діліп

2
@Dilip Чи знаєте ви, що ви також можете просити коментарі, на які ви погоджуєтесь? Це лише для того, щоб розділ коментарів був чистим, щоб не було багато коментарів, які насправді однакові.
пірхо

2

Замість того, щоб переглядати всі представлення даних або переоцінювати dispatchTouchEvent.

Чому б просто не змінити функцію onUserInteraction () діяльності, це гарантуватиме відхилення клавіатури кожного разу, коли користувач натискатиме її за межами EditText.

Працюватиме навіть тоді, коли EditText знаходиться всередині scrollView.

@Override
public void onUserInteraction() {
    if (getCurrentFocus() != null) {
        InputMethodManager imm = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE);
        imm.hideSoftInputFromWindow(getCurrentFocus().getWindowToken(), 0);
    }
}

це краща відповідь
ovluca

Так! Це надзвичайно чиста відповідь. І якщо ви створили 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);
}

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, і кожен контейнер Відносні / лінійні макети повинні це реалізувати.

**** Візьміть до уваги лише цей основний контейнер повинен реалізувати цей клас (Для оптимізації) ****

і реалізуйте це так:

Parent.setOnClickListener( new DismissKeyboardListener(this) ); 

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

--- великі пальці, якщо це допоможе тобі ... --- привітає Ральф ---


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

Щось тут не виглядає; ви призначаєте і те, 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);
  }

Зроблено

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.