Управление видимостью способа ввода

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

Для большинства приложений этих режимов работы по умолчанию достаточно. Однако в некоторых случаях вам может потребоваться больший контроль над видимостью метода ввода и тем, как он влияет на макет. В этом уроке объясняется, как управлять видимостью метода ввода и реагировать на нее.

Показывать экранную клавиатуру при запуске действия

Хотя Android фокусируется на первом текстовом поле в макете при запуске действия, экранная клавиатура не отображается. Такое поведение уместно, поскольку ввод текста может не быть основной задачей в действии. Однако если ввод текста действительно является основной задачей, например на экране входа в систему, то, вероятно, вы захотите, чтобы экранная клавиатура появлялась по умолчанию.

Чтобы способ ввода отображался при запуске действия, добавьте атрибут android:windowSoftInputMode к элементу <activity> со значением "stateVisible". Пример:

<application ... >
    <activity
        android:windowSoftInputMode="stateVisible" ... >
        ...
    </activity>
   ...
</application>

Укажите, как должен реагировать интерфейс

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

Чтобы указать предпочтительный вариант обработки в действии, используйте атрибут android:windowSoftInputMode в элементе <activity> манифеста с одним из значений adjust.

Например, чтобы система изменяла размер макета в соответствии с доступным пространством, используйте "adjustResize". В этом случае весь контент макета будет доступен, даже если для его просмотра потребуется прокрутка.

<application ... >
   <activity
       android:windowSoftInputMode="adjustResize" ... >
       ...
   </activity>
   ...
</application>

Вы можете объединить спецификацию корректировки со спецификацией начальной видимости экранной клавиатуры из предыдущего раздела:

<activity
    android:windowSoftInputMode="stateVisible|adjustResize" ... >
    ...
</activity>

Указывать "adjustResize" важно, если в интерфейсе есть элементы управления, которые могут понадобиться пользователю сразу после ввода текста или во время него. Например, если вы используете относительный макет, чтобы разместить панель кнопок в нижней части экрана, то при использовании "adjustResize" размер макета изменится так, чтобы панель кнопок отображалась над экранной клавиатурой.

Показывать экранную клавиатуру по запросу

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

Например, следующий метод принимает View, в котором пользователь должен что-то ввести, вызывает requestFocus(), чтобы сфокусироваться на нем, а затем вызывает showSoftInput(), чтобы открыть способ ввода:

Kotlin

fun showSoftKeyboard(view: View) {
   if (view.requestFocus()) {
       val imm = getSystemService(InputMethodManager::class.java)
       imm.showSoftInput(view, InputMethodManager.SHOW_IMPLICIT)
   }
}

Java

public void showSoftKeyboard(View view) {
   if (view.requestFocus()) {
       InputMethodManager imm = getSystemService(InputMethodManager.class);
       imm.showSoftInput(view, InputMethodManager.SHOW_IMPLICIT);
   }
}

Надежный показ экранной клавиатуры

В некоторых ситуациях, например при запуске действия, использование InputMethodManager.showSoftInput() для отображения виртуальной клавиатуры может привести к тому, что виртуальная клавиатура не будет видна пользователю.

Видимость экранной клавиатуры при использовании showSoftInput() зависит от следующих условий:

  • Представление уже должно быть подключено к виртуальной клавиатуре. Для этого окно должно быть в фокусе, а редактор должен запросить фокус с помощью View.requestFocus().

  • На видимость также могут влиять атрибут android:windowSoftInputMode и флаги, используемые showSoftInput().

В некоторых случаях, например при запуске действия, некоторые из этих условий не выполняются. Система не считает представление связанным с виртуальной клавиатурой, игнорирует вызов showSoftInput(), и клавиатура не показывается пользователю.

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

  • (Рекомендуется) Используйте WindowInsetsControllerCompat. Этот объект показывает экранную клавиатуру во время Activity.onCreate(), как показано в следующем фрагменте кода. Вызов гарантированно будет запланирован после того, как окно окажется в фокусе.

Kotlin

editText.requestFocus()
WindowCompat.getInsetsController(window, editText)!!.show(WindowInsetsCompat.Type.ime())

Java

editText.requestFocus();
WindowCompat.getInsetsController(getWindow(), editText).show(WindowInsetsCompat.Type.ime());
  • Post a runnable. Это гарантирует, что приложение будет ждать получения события фокуса окна от View.onWindowFocusChanged(), прежде чем вызывать showSoftInput().

Kotlin

class MyEditText : EditText() {
  ...
  override fun onWindowFocusChanged(hasWindowFocus: Boolean) {
    if (hasWindowFocus) {
      requestFocus()
      post {
        val imm: InputMethodManager = getSystemService(InputMethodManager::class.java)
        imm.showSoftInput(this, 0)
      }
    }
  }
}

Java

public class MyEditText extends EditText {
  ...
  @Override
  public void onWindowFocusChanged(boolean hasWindowFocus) {
    if (hasWindowFocus) {
      requestFocus();
      post(() -> {
        InputMethodManager imm = getSystemService(InputMethodManager.class);
        imm.showSoftInput(this, 0);
      });
    }
  }
}

Будьте осторожны с флагами видимости во время выполнения

При переключении видимости экранной клавиатуры во время выполнения не передавайте в эти методы определенные значения флагов. Например, если приложение ожидает, что экранная клавиатура появится при вызове View.getWindowInsetsController().show(ime()) в Activity.onCreate() во время запуска действия, разработчикам приложения следует быть осторожными и не устанавливать флаги SOFT_INPUT_STATE_HIDDEN или SOFT_INPUT_STATE_ALWAYS_HIDDEN во время первоначального запуска, чтобы экранная клавиатура не скрылась неожиданно.

Обычно система скрывает экранную клавиатуру автоматически.

В большинстве ситуаций система сама скрывает экранную клавиатуру. Это может быть любой из следующих случаев:

  • Пользователь завершает задачу в текстовом поле.
  • Пользователь нажимает кнопку "Назад" или использует жесты смахивания для обратной навигации.
  • Пользователь переходит в другое приложение, в котором при получении фокуса установлены флаги SOFT_INPUT_STATE_HIDDEN или SOFT_INPUT_STATE_ALWAYS_HIDDEN.

Скрывать экранную клавиатуру вручную на основе предыдущего поведения системы

В некоторых ситуациях приложение должно скрывать экранную клавиатуру вручную, например когда текстовое поле теряет фокус в View.OnFocusChangeListener.onFocusChange. Используйте этот метод с осторожностью: неожиданное закрытие экранной клавиатуры ухудшает взаимодействие с пользователем.

Если ваше приложение скрывает экранную клавиатуру вручную, вам нужно знать, была ли она показана явно или неявно.

  • Экранная клавиатура считается явно показанной после вызова showSoftInput().

  • Экранная клавиатура считается показанной неявно, если выполняется одно из следующих условий:

    • При применении android:windowSoftInputMode система показала экранную клавиатуру.
    • Ваше приложение передало SHOW_IMPLICIT в showSoftInput().

Обычно метод hideSoftInputFromWindow() скрывает экранную клавиатуру независимо от того, как она была вызвана. Однако с помощью флага HIDE_IMPLICIT_ONLY можно ограничить его действие, чтобы он скрывал только неявно вызванную экранную клавиатуру.

Показывать диалоговое окно или оверлей поверх экранной клавиатуры

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

В следующих разделах описаны доступные варианты.

Убедитесь, что флаги окна экранной клавиатуры, нацеленной на окно, обрабатываются правильно и соответствуют следующим требованиям к вертикальному порядку (z-слой):

  • Без флагов (обычный случай): за слоем экранной клавиатуры, может принимать текст.
  • FLAG_NOT_FOCUSABLE : поверх экранной клавиатуры, но не может получать текст.
  • FLAG_ALT_FOCUSABLE_IM : находится поверх слоя экранной клавиатуры, может быть в фокусе, но не связана с экранной клавиатурой. Также блокирует подключение к экранной клавиатуре всех представлений, расположенных под ним. Это полезно для показа диалогового окна приложения, в котором не используется ввод текста над слоем экранной клавиатуры.
  • FLAG_NOT_FOCUSABLE и FLAG_ALT_FOCUSABLE_IM – за слоем экранной клавиатуры, но не могут получать текст.
  • FLAG_NOT_FOCUSABLE и FLAG_NOT_TOUCH_MODAL – поверх экранной клавиатуры. События касания передаются через окно на экранную клавиатуру.

Создать диалоговое окно

Чтобы диалоговое окно оставалось поверх экранной клавиатуры и не получало фокус, используйте флаг FLAG_ALT_FOCUSABLE_IM.

Kotlin

val content = TextView(this)
content.text = "Non-editable dialog on top of soft keyboard"
content.gravity = Gravity.CENTER
val builder = AlertDialog.Builder(this)
  .setTitle("Soft keyboard layering demo")
  .setView(content)
mDialog = builder.create()
mDialog!!.window!!
  .addFlags(WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM)
mDialog!!.show()

Java

TextView content = new TextView(this);
content.setText("Non-editable dialog on top of soft keyboard");
content.setGravity(Gravity.CENTER);
final AlertDialog.Builder builder = new AlertDialog.Builder(this)
    .setTitle("Soft keyboard layering demo")
    .setView(content);
mDialog = builder.create();
mDialog.getWindow().addFlags(FLAG_ALT_FOCUSABLE_IM);
mDialog.show();

Как создать представление наложения

Создайте оверлей, указав тип окна TYPE_APPLICATION_OVERLAY и флаг окна FLAG_ALT_FOCUSABLE_IM, для действия, связанного с экранной клавиатурой.

Kotlin

val params = WindowManager.LayoutParams(
  width,  /* Overlay window width */
  height,  /* Overlay window height */
  WindowManager.LayoutParams.TYPE_APPLICATION, /* Overlay window type */
  WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM /* No need to allow for text input on top of the soft keyboard */
    or WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL,  /* Allow touch event send to soft keyboard behind the overlay */
  PixelFormat.TRANSLUCENT
)
params.title = "Overlay window"
mOverlayView!!.layoutParams = params
windowManager.addView(mOverlayView, params)

Java

WindowManager.LayoutParams params = new WindowManager.LayoutParams(
    width, /* Overlay window width */
    height, /* Overlay window height */
    TYPE_APPLICATION, /* Overlay window type */
    FLAG_ALT_FOCUSABLE_IM /* No need to allow for text input on top of the soft keyboard */
        | FLAG_NOT_TOUCH_MODAL, /* Allow touch event send to soft keyboard behind the overlay */
    PixelFormat.TRANSLUCENT);
params.setTitle("Overlay window");
mOverlayView.setLayoutParams(params);
getWindowManager().addView(mOverlayView, params);

Показ диалогового окна или представления под экранной клавиатурой

В приложении может потребоваться создать диалоговое окно со следующими свойствами:

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

В этом случае у приложения есть несколько вариантов. Эти варианты описаны в разделах ниже.

Создать диалоговое окно

Создайте диалоговое окно, установив флажки FLAG_NOT_FOCUSABLE и FLAG_ALT_FOCUSABLE_IM:

Kotlin

val content = TextView(this)
content.text = "Non-editable dialog behind soft keyboard"
content.gravity = Gravity.CENTER
val builder = AlertDialog.Builder(this)
  .setTitle("Soft keyboard layering demo")
  .setView(content)
mDialog = builder.create()
mDialog!!.window!!
  .addFlags(FLAG_NOT_FOCUSABLE or FLAG_ALT_FOCUSABLE_IM)
mDialog!!.show()

Java

TextView content = new TextView(this);
content.setText("Non-editable dialog behind soft keyboard");
content.setGravity(Gravity.CENTER);
final AlertDialog.Builder builder = new AlertDialog.Builder(this)
    .setTitle("Soft keyboard layering demo")
    .setView(content);

mDialog = builder.create();
mDialog.getWindow()
    .addFlags(FLAG_NOT_FOCUSABLE | FLAG_ALT_FOCUSABLE_IM);
mDialog.show();

Как создать представление наложения

Создайте оверлей, установив флаги окна FLAG_NOT_FOCUSABLE и FLAG_ALT_FOCUSABLE_IM:

Kotlin

val params = WindowManager.LayoutParams(
  width,  /* Overlay window width */
  height,  /* Overlay window height */
  WindowManager.LayoutParams.TYPE_APPLICATION,  /* Overlay window type */
  WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
      or WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM,
  PixelFormat.TRANSLUCENT
)
params.title = "Overlay window"
mOverlayView!!.layoutParams = params
windowManager.addView(mOverlayView, params)

Java

WindowManager.LayoutParams params = new WindowManager.LayoutParams(
    width, /* Overlay window width */
    height, /* Overlay window height */
    TYPE_APPLICATION, /* Overlay window type */
    FLAG_NOT_FOCUSABLE | FLAG_ALT_FOCUSABLE_IM,
    PixelFormat.TRANSLUCENT);
params.setTitle("Overlay window");
mOverlayView.setLayoutParams(params);
getWindowManager().addView(mOverlayView, params);