Обработка видимости метода ввода

Когда фокус ввода перемещается в редактируемое текстовое поле или из него, 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() , чтобы открыть метод ввода:

Котлин

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

Ява

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() , как показано в следующем фрагменте кода. Вызов гарантированно будет запланирован после того, как окно будет сфокусировано.

Котлин

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

Ява

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

Котлин

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

Ява

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 , чтобы диалоговое окно оставалось поверх виртуальной клавиатуры и чтобы виртуальная клавиатура не получала фокус:

Котлин

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

Ява

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 с помощью целевого действия программной клавиатуры.

Котлин

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)

Ява

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 :

Котлин

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

Ява

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 :

Котлин

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)

Ява

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