Как можно программно закрыть или скрыть виртуальную клавиатуру Android?
В моём макете Android есть EditText и Button. После ввода текста в EditText и нажатия на Button я хочу скрыть виртуальную клавиатуру, когда пользователь коснется вне её области. Не могли бы вы предоставить простой пример кода, демонстрирующий, как реализовать эту функциональность?
Программное скрытие программной клавиатуры Android
Чтобы программatically скрыть программную клавиатуру Android, можно использовать сервис InputMethodManager в сочетании с методами для обнаружения касаний вне вашего EditText. Наиболее распространенный подход включает вызов hideSoftInputFromWindow() с токеном окна EditText и переопределение событий касания для обнаружения, когда пользователь нажимает вне поля ввода.
Содержание
- Базовые методы скрытия клавиатуры
- Скрытие клавиатуры по нажатию кнопки
- Скрытие клавиатуры при касании вне поля
- Альтернативные подходы
- Полный пример реализации
Базовые методы скрытия клавиатуры
Основной подход к скрытию программной клавиатуры involves использование сервиса InputMethodManager Android. Этот сервис предоставляет системный доступ к методам ввода, включая программную клавиатуру.
// Метод для скрытия клавиатуры из любого контекста
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);
}
}
Этот метод работает следующим образом:
- Получение InputSystemService из текущего контекста
- Вызов
hideSoftInputFromWindow()с токеном окна представления - Второй параметр (0) представляет флаги, которые могут быть настроены для разных поведений
Для скрытия клавиатуры specifically из EditText можно использовать:
// Скрыть клавиатуру из EditText
EditText editText = findViewById(R.id.editText);
InputMethodManager imm = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE);
imm.hideSoftInputFromWindow(editText.getWindowToken(), 0);
Скрытие клавиатуры по нажатию кнопки
Чтобы скрыть клавиатуру при нажатии кнопки, можно установить OnClickListener на вашей Button, который вызывает метод скрытия:
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();
});
Более комплексный подход, который обрабатывает как нажатия кнопок, так и события касания:
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:
@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);
}
Этот подход работает следующим образом:
- Проверка, является ли текущее сфокусированное представление EditText
- Получение глобально видимого прямоугольника EditText
- Сравнение координат касания с границами EditText
- Скрытие клавиатуры, если касание произошло вне EditText
Реализация на 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:
<activity
android:name=".MainActivity"
android:windowSoftInputMode="stateHidden|adjustPan" />
Распространенные значения для windowSoftInputMode:
stateHidden: Клавиатура скрыта при запуске активностиstateAlwaysHidden: Клавиатура всегда скрытаadjustPan: Контент сдвигается при появлении клавиатурыadjustResize: Окно изменяет размер при появлении клавиатуры
Использование ViewTreeObserver
Другой подход - использование ViewTreeObserver для обнаружения изменений макета:
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-тестах:
// Использование Espresso
onView(withId(R.id.editText)).perform(closeSoftKeyboard());
Полный пример реализации
Вот полный, практический пример реализации, объединяющий несколько подходов:
MainActivity.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 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:
<activity
android:name=".MainActivity"
android:windowSoftInputMode="stateHidden|adjustPan">
</activity>
Источники
- Hiding the Android Soft Keyboard Programmatically on Button Click and Touch Outside
- How to hide soft keyboard on android after clicking outside EditText? - Stack Overflow
- How can I close/hide the Android soft keyboard programmatically? - Stack Overflow
- A Guide to Programmatically Hide the Android Soft Keyboard
- How to hide a soft keyboard on android after clicking outside EditText using Kotlin? - Tutorialspoint
Заключение
Чтобы программatically скрыть программную клавиатуру Android, у вас есть несколько эффективных подходов:
- Используйте InputMethodManager: Наиболее надежный метод - вызов
hideSoftInputFromWindow()с токеном окна представления - Переопределите dispatchTouchEvent: Предоставляет комплексную обработку событий касания во всей активности
- Комбинируйте несколько подходов: Используйте слушатели нажатий кнопок, слушатели касания и переопределения для надежного поведения
- Настройте windowSoftInputMode: Установите соответствующие настройки манифеста для начального состояния клавиатуры
Ключевые моменты для рассмотрения:
- Всегда снимайте фокус с EditText после скрытия клавиатуры
- Обрабатывайте как нажатия кнопок, так и события касания для полного пользовательского опыта
- Учитывайте доступность и крайние случаи в вашей реализации
- Тестируйте на разных версиях Android и размерах экранов
Для большинства приложений комбинация переопределения dispatchTouchEvent и слушателей нажатий кнопок предоставляет наиболее полное решение для программального скрытия программной клавиатуры, когда пользователи взаимодействуют с вашим пользовательским интерфейсом.