НейроАгент

Как скрыть программную клавиатуру Android: Полное руководство

Узнайте несколько способов программного скрытия программной клавиатуры Android при нажатии на кнопку и касании вне поля. Полные примеры кода с InputMethodManager и обработкой касаний.

Вопрос

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

В моём макете Android есть EditText и Button. После ввода текста в EditText и нажатия на Button я хочу скрыть виртуальную клавиатуру, когда пользователь коснется вне её области. Не могли бы вы предоставить простой пример кода, демонстрирующий, как реализовать эту функциональность?

НейроАгент

Программное скрытие программной клавиатуры Android

Чтобы программatically скрыть программную клавиатуру Android, можно использовать сервис InputMethodManager в сочетании с методами для обнаружения касаний вне вашего EditText. Наиболее распространенный подход включает вызов hideSoftInputFromWindow() с токеном окна EditText и переопределение событий касания для обнаружения, когда пользователь нажимает вне поля ввода.

Содержание

Базовые методы скрытия клавиатуры

Основной подход к скрытию программной клавиатуры involves использование сервиса InputMethodManager Android. Этот сервис предоставляет системный доступ к методам ввода, включая программную клавиатуру.

java
// Метод для скрытия клавиатуры из любого контекста
public static void hideKeyboard(Context context, View view) {
    InputMethodManager imm = (InputMethodManager) context.getSystemService(Context.INPUT_METHOD_SERVICE);
    if (imm != null) {
        imm.hideSoftInputFromWindow(view.getWindowToken(), 0);
    }
}

Этот метод работает следующим образом:

  1. Получение InputSystemService из текущего контекста
  2. Вызов hideSoftInputFromWindow() с токеном окна представления
  3. Второй параметр (0) представляет флаги, которые могут быть настроены для разных поведений

Для скрытия клавиатуры specifically из EditText можно использовать:

java
// Скрыть клавиатуру из EditText
EditText editText = findViewById(R.id.editText);
InputMethodManager imm = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE);
imm.hideSoftInputFromWindow(editText.getWindowToken(), 0);

Скрытие клавиатуры по нажатию кнопки

Чтобы скрыть клавиатуру при нажатии кнопки, можно установить OnClickListener на вашей Button, который вызывает метод скрытия:

java
Button submitButton = findViewById(R.id.submitButton);
submitButton.setOnClickListener(v -> {
    // Скрыть клавиатуру при нажатии кнопки
    InputMethodManager imm = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE);
    imm.hideSoftInputFromWindow(currentView.getWindowToken(), 0);
    
    // Опционально: Снять фокус с EditText
    EditText editText = findViewById(R.id.editText);
    editText.clearFocus();
});

Более комплексный подход, который обрабатывает как нажатия кнопок, так и события касания:

java
public class MainActivity extends AppCompatActivity {
    private EditText editText;
    
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        
        editText = findViewById(R.id.editText);
        Button submitButton = findViewById(R.id.submitButton);
        
        // Скрыть клавиатуру при нажатии кнопки
        submitButton.setOnClickListener(v -> hideKeyboard());
        
        // Настроить слушатель касаний для всего макета
        findViewById(R.id.rootLayout).setOnTouchListener(this::onRootLayoutTouch);
    }
    
    private void hideKeyboard() {
        InputMethodManager imm = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE);
        imm.hideSoftInputFromWindow(editText.getWindowToken(), 0);
        editText.clearFocus();
    }
    
    private boolean onRootLayoutTouch(View v, MotionEvent event) {
        if (event.getAction() == MotionEvent.ACTION_DOWN) {
            if (isTouchOutsideEditText(event)) {
                hideKeyboard();
                return true;
            }
        }
        return false;
    }
    
    private boolean isTouchOutsideEditText(MotionEvent event) {
        int[] editTextLocation = new int[2];
        editText.getLocationOnScreen(editTextLocation);
        
        float touchX = event.getRawX();
        float touchY = event.getRawY();
        
        return touchX < editTextLocation[0] || 
               touchX > editTextLocation[0] + editText.getWidth() ||
               touchY < editTextLocation[1] || 
               touchY > editTextLocation[1] + editText.getHeight();
    }
}

Скрытие клавиатуры при касании вне поля

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

java
@Override
public boolean dispatchTouchEvent(MotionEvent event) {
    if (event.getAction() == MotionEvent.ACTION_DOWN) {
        View v = getCurrentFocus();
        if (v instanceof EditText) {
            Rect outRect = new Rect();
            v.getGlobalVisibleRect(outRect);
            if (!outRect.contains((int)event.getRawX(), (int)event.getRawY())) {
                // Пользователь нажал вне EditText, скрыть клавиатуру
                v.clearFocus();
                InputMethodManager imm = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE);
                imm.hideSoftInputFromWindow(v.getWindowToken(), 0);
            }
        }
    }
    return super.dispatchTouchEvent(event);
}

Этот подход работает следующим образом:

  1. Проверка, является ли текущее сфокусированное представление EditText
  2. Получение глобально видимого прямоугольника EditText
  3. Сравнение координат касания с границами EditText
  4. Скрытие клавиатуры, если касание произошло вне EditText

Реализация на Kotlin:

kotlin
override fun dispatchTouchEvent(ev: MotionEvent?): Boolean {
    if (ev?.action == MotionEvent.ACTION_DOWN) {
        currentFocus?.let { view ->
            if (view is EditText) {
                val rect = Rect()
                view.getGlobalVisibleRect(rect)
                if (!rect.contains(ev.rawX.toInt(), ev.rawY.toInt())) {
                    view.clearFocus()
                    getSystemService(Context.INPUT_METHOD_SERVICE)?.let { imm ->
                        imm.hideSoftInputFromWindow(view.windowToken, 0)
                    }
                }
            }
        }
    }
    return super.dispatchTouchEvent(ev)
}

Альтернативные подходы

Использование режима ввода окна

Вы можете настроить поведение клавиатуры в файле AndroidManifest.xml:

xml
<activity
    android:name=".MainActivity"
    android:windowSoftInputMode="stateHidden|adjustPan" />

Распространенные значения для windowSoftInputMode:

  • stateHidden: Клавиатура скрыта при запуске активности
  • stateAlwaysHidden: Клавиатура всегда скрыта
  • adjustPan: Контент сдвигается при появлении клавиатуры
  • adjustResize: Окно изменяет размер при появлении клавиатуры

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

Другой подход - использование ViewTreeObserver для обнаружения изменений макета:

java
editText.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
    @Override
    public void onGlobalLayout() {
        Rect r = new Rect();
        editText.getWindowVisibleDisplayFrame(r);
        int screenHeight = getWindow().getDecorView().getRootView().getHeight();
        int keypadHeight = screenHeight - r.bottom;
        
        if (keypadHeight > screenHeight * 0.15) { // Порог 15%
            // Клавиатура видна
        } else {
            // Клавиатура скрыта
        }
    }
});

Использование Espresso для тестирования

Если вам нужно скрыть клавиатуру в UI-тестах:

java
// Использование Espresso
onView(withId(R.id.editText)).perform(closeSoftKeyboard());

Полный пример реализации

Вот полный, практический пример реализации, объединяющий несколько подходов:

MainActivity.java:

java
public class MainActivity extends AppCompatActivity {
    private EditText nameEditText, emailEditText;
    private Button submitButton;
    private ScrollView rootLayout;
    
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        
        // Инициализация представлений
        nameEditText = findViewById(R.id.nameEditText);
        emailEditText = findViewById(R.id.emailEditText);
        submitButton = findViewById(R.id.submitButton);
        rootLayout = findViewById(R.id.rootLayout);
        
        // Настройка слушателей
        setupClickListeners();
    }
    
    private void setupClickListeners() {
        // Скрыть клавиатуру при нажатии кнопки
        submitButton.setOnClickListener(v -> {
            hideKeyboard();
            handleSubmit();
        });
        
        // Скрыть клавиатуру при нажатии вне полей EditText
        rootLayout.setOnTouchListener(this::onRootLayoutTouch);
    }
    
    private void hideKeyboard() {
        hideKeyboard(nameEditText);
        hideKeyboard(emailEditText);
    }
    
    private void hideKeyboard(View view) {
        InputMethodManager imm = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE);
        if (imm != null) {
            imm.hideSoftInputFromWindow(view.getWindowToken(), 0);
        }
        view.clearFocus();
    }
    
    private boolean onRootLayoutTouch(View v, MotionEvent event) {
        if (event.getAction() == MotionEvent.ACTION_DOWN) {
            if (isTouchOutsideEditTexts(event)) {
                hideKeyboard();
                return true;
            }
        }
        return false;
    }
    
    private boolean isTouchOutsideEditTexts(MotionEvent event) {
        int[] nameLocation = new int[2];
        int[] emailLocation = new int[2];
        
        nameEditText.getLocationOnScreen(nameLocation);
        emailEditText.getLocationOnScreen(emailLocation);
        
        float touchX = event.getRawX();
        float touchY = event.getRawY();
        
        // Проверить, находится ли касание вне обоих полей EditText
        boolean outsideName = touchX < nameLocation[0] || 
                             touchX > nameLocation[0] + nameEditText.getWidth() ||
                             touchY < nameLocation[1] || 
                             touchY > nameLocation[1] + nameEditText.getHeight();
        
        boolean outsideEmail = touchX < emailLocation[0] || 
                              touchX > emailLocation[0] + emailEditText.getWidth() ||
                              touchY < emailLocation[1] || 
                              touchY > emailLocation[1] + emailEditText.getHeight();
        
        return outsideName && outsideEmail;
    }
    
    private void handleSubmit() {
        String name = nameEditText.getText().toString().trim();
        String email = emailEditText.getText().toString().trim();
        
        if (name.isEmpty() || email.isEmpty()) {
            Toast.makeText(this, "Пожалуйста, заполните все поля", Toast.LENGTH_SHORT).show();
            return;
        }
        
        // Обработка отправки формы
        Toast.makeText(this, "Форма успешно отправлена!", Toast.LENGTH_SHORT).show();
    }
    
    @Override
    public boolean dispatchTouchEvent(MotionEvent event) {
        if (event.getAction() == MotionEvent.ACTION_DOWN) {
            View v = getCurrentFocus();
            if (v instanceof EditText) {
                Rect outRect = new Rect();
                v.getGlobalVisibleRect(outRect);
                if (!outRect.contains((int)event.getRawX(), (int)event.getRawY())) {
                    v.clearFocus();
                    hideKeyboard(v);
                }
            }
        }
        return super.dispatchTouchEvent(event);
    }
}

activity_main.xml:

xml
<?xml version="1.0" encoding="utf-8"?>
<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/rootLayout"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:padding="16dp">
    
    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="vertical">
        
        <EditText
            android:id="@+id/nameEditText"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:hint="Введите ваше имя"
            android:inputType="textPersonName"/>
        
        <EditText
            android:id="@+id/emailEditText"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_marginTop="16dp"
            android:hint="Введите ваш email"
            android:inputType="textEmailAddress"/>
        
        <Button
            android:id="@+id/submitButton"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_marginTop="24dp"
            android:text="Отправить"/>
        
    </LinearLayout>
    
</ScrollView>

AndroidManifest.xml:

xml
<activity
    android:name=".MainActivity"
    android:windowSoftInputMode="stateHidden|adjustPan">
</activity>

Источники

  1. Hiding the Android Soft Keyboard Programmatically on Button Click and Touch Outside
  2. How to hide soft keyboard on android after clicking outside EditText? - Stack Overflow
  3. How can I close/hide the Android soft keyboard programmatically? - Stack Overflow
  4. A Guide to Programmatically Hide the Android Soft Keyboard
  5. How to hide a soft keyboard on android after clicking outside EditText using Kotlin? - Tutorialspoint

Заключение

Чтобы программatically скрыть программную клавиатуру Android, у вас есть несколько эффективных подходов:

  1. Используйте InputMethodManager: Наиболее надежный метод - вызов hideSoftInputFromWindow() с токеном окна представления
  2. Переопределите dispatchTouchEvent: Предоставляет комплексную обработку событий касания во всей активности
  3. Комбинируйте несколько подходов: Используйте слушатели нажатий кнопок, слушатели касания и переопределения для надежного поведения
  4. Настройте windowSoftInputMode: Установите соответствующие настройки манифеста для начального состояния клавиатуры

Ключевые моменты для рассмотрения:

  • Всегда снимайте фокус с EditText после скрытия клавиатуры
  • Обрабатывайте как нажатия кнопок, так и события касания для полного пользовательского опыта
  • Учитывайте доступность и крайние случаи в вашей реализации
  • Тестируйте на разных версиях Android и размерах экранов

Для большинства приложений комбинация переопределения dispatchTouchEvent и слушателей нажатий кнопок предоставляет наиболее полное решение для программального скрытия программной клавиатуры, когда пользователи взаимодействуют с вашим пользовательским интерфейсом.