Как создать способ ввода

Редактор метода ввода (IME) – это элемент управления, который позволяет пользователям вводить текст. Android предоставляет расширяемую платформу для методов ввода, которая позволяет приложениям предлагать пользователям альтернативные способы ввода, например экранную клавиатуру или голосовой ввод. После установки пользователь может выбрать один из них в системных настройках и использовать его во всей системе. Одновременно можно включить только один метод ввода.

Чтобы добавить IME в систему Android, создайте приложение для Android, содержащее класс, который расширяет InputMethodService. Кроме того, обычно создается действие "Настройки", которое передает параметры сервису IME. Вы также можете определить интерфейс настроек, который будет показываться как часть системных настроек.

На этой странице рассказывается следующее:

Если вы ещё не работали с IME, сначала прочитайте статью Методы ввода на экране.

Жизненный цикл IME

На приведенной ниже схеме показан жизненный цикл IME.

Изображение, на котором показан жизненный цикл IME.
Рисунок 1. Жизненный цикл IME

В следующих разделах описано, как реализовать интерфейс и код, связанные с IME, который следует этому жизненному циклу.

Как объявить компоненты IME в манифесте

В системе Android IME – это приложение для Android, содержащее специальный сервис IME. В файле манифеста приложения необходимо объявить сервис, запросить необходимые разрешения, предоставить фильтр интентов, соответствующий действию action.view.InputMethod, и указать метаданные, определяющие характеристики IME. Кроме того, чтобы предоставить пользователю интерфейс для изменения поведения IME, вы можете определить активность "Настройки", которую можно запустить из системных настроек.

В приведенном ниже фрагменте кода объявлен сервис IME. Он запрашивает разрешение BIND_INPUT_METHOD, чтобы сервис мог подключить IME к системе, настраивает фильтр интентов, соответствующий действию android.view.InputMethod, и определяет метаданные для IME:

<!-- Declares the input method service. -->
<service android:name="FastInputIME"
    andro@id:label="string/fast_input_label"
    android:permission="android.perm>issio<n.BIND_INPUT_>METHOD&qu<ot;
    intent-filter
        action android:nam>e=&qu<ot;android.vie>w.Inp<utMethod" /
    /intent-filter
    meta-data android:name="andro@id.view.im&qu>o<t;
     >          android:resource="xml/method" /
/service

В следующем фрагменте кода объявляется объект activity для настроек IME. У него есть фильтр интентов для ACTION_MAIN, который указывает, что это Activity является основной точкой входа для приложения IME:

<!-- Optional: an activity for controlling the IME settings. -->
<activity android:name="FastInputIMESettings"
    andro@id:label="string/fast_>input<_settings&quo>t;
    in<tent-filter
        action android:name="and>roid.<intent.action.>M<AIN">/
    /intent-filter
/activity

Вы также можете предоставить доступ к настройкам IME прямо из ее интерфейса.

API способа ввода

Классы, относящиеся к методам ввода, находятся в пакетах android.inputmethodservice и android.view.inputmethod. Класс KeyEvent важен для обработки символов клавиатуры.

Центральная часть IME – это компонент сервиса, класс, который расширяет InputMethodService. Помимо реализации стандартного жизненного цикла сервиса, этот класс содержит обратные вызовы для предоставления пользовательского интерфейса IME, обработки ввода данных пользователем и передачи текста в поле, которое находится в фокусе. По умолчанию класс InputMethodService предоставляет большую часть реализации для управления состоянием и видимостью IME, а также для взаимодействия с текущим полем ввода.

Также важны следующие классы:

BaseInputConnection
Определяет канал связи от InputMethod к приложению, которое получает входные данные. Он используется для чтения текста вокруг курсора, добавления текста в текстовое поле и отправки необработанных событий нажатия клавиш в приложение. Приложения должны расширять этот класс, а не реализовывать базовый интерфейс InputConnection.
KeyboardView
Расширение View, которое отображает клавиатуру и реагирует на события ввода данных пользователем. Раскладка клавиатуры задается экземпляром Keyboard, который можно определить в XML-файле.

Как создать интерфейс способа ввода

У IME есть два основных визуальных элемента: окно ввода и окно вариантов. Вам нужно реализовать только те элементы, которые относятся к разрабатываемому вами способу ввода.

Входное представление

Входное представление – это интерфейс, в котором пользователь вводит текст с помощью нажатий клавиш, рукописного ввода или жестов. Когда IME показывается впервые, система вызывает обратный вызов onCreateInputView(). В реализации этого метода создайте макет, который вы хотите отобразить в окне IME, и верните макет системе. Во фрагменте кода ниже приведен пример реализации метода onCreateInputView().

Kotlin

override fun onCreateInputView(): View {
    return layoutInflater.inflate(R.layout.input, null).apply {
        if (this is MyKeyboardView) {
            setOnKeyboardActionListener(this@MyInputMethod)
            keyboard = latinKeyboard
        }
    }
}

Java

@Override
public View onCreateInputView() {
    MyKeyboardView inputView =
        (MyKeyboardView) getLayoutInflater().inflate(R.layout.input, null);

    inputView.setOnKeyboardActionListener(this);
    inputView.setKeyboard(latinKeyboard);

    return inputView;
}

В этом примере MyKeyboardView – это экземпляр пользовательской реализации KeyboardView, который отрисовывает Keyboard.

Предварительная версия

Окно кандидатов – это интерфейс, в котором IME показывает возможные исправления или подсказки слов, которые пользователь может выбрать. В жизненном цикле IME система вызывает метод onCreateCandidatesView(), когда готова показать окно вариантов. В реализации этого метода верните макет, в котором показываются подсказки слов, или значение null, если вы не хотите показывать ничего. Ответ null используется по умолчанию, поэтому вам не нужно реализовывать его, если вы не предоставляете подсказки.

Рекомендации по дизайну интерфейса

В этом разделе описаны некоторые аспекты дизайна интерфейса для методов ввода.

Как работать с разными размерами экрана

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

Как обрабатывать разные типы входных данных

В текстовых полях Android можно задать определенный тип ввода, например произвольный текст, числа, URL, адреса электронной почты и поисковые запросы. При реализации нового метода ввода необходимо определить тип ввода для каждого поля и предоставить для него подходящий интерфейс. Однако вам не нужно настраивать IME так, чтобы она проверяла, ввел ли пользователь допустимый текст для типа ввода. Это ответственность приложения, которому принадлежит текстовое поле.

Например, вот интерфейс, который предоставляет Latin IME для ввода текста на платформе Android:

Изображение, на котором показан ввод текста с помощью латинского метода ввода
Рисунок 2. Ввод текста с помощью латинской IME.

Ниже показан интерфейс, который Latin IME предоставляет для платформы Android: ввод чисел:

Изображение, на котором показан ввод чисел с помощью латинской раскладки IME
Рисунок 3. Ввод цифр с помощью латинской IME.

Когда фокус ввода переходит на поле ввода и запускается IME, система вызывает метод onStartInputView(), передавая ему объект EditorInfo, который содержит сведения о типе ввода и других атрибутах текстового поля. В этом объекте поле inputType содержит тип ввода текстового поля.

Поле inputType представляет собой int, содержащий битовые шаблоны для различных настроек типа ввода. Чтобы проверить тип ввода текстового поля, замаскируйте его константой TYPE_MASK_CLASS, как показано ниже:

Kotlin

inputType and InputType.TYPE_MASK_CLASS

Java

inputType & InputType.TYPE_MASK_CLASS

Шаблон битов типа входных данных может иметь одно из нескольких значений, в том числе:

TYPE_CLASS_NUMBER
Текстовое поле для ввода чисел. Как показано на рисунке 3, для полей этого типа в Latin IME отображается цифровая клавиатура.
TYPE_CLASS_DATETIME
Текстовое поле для ввода даты и времени.
TYPE_CLASS_PHONE
Текстовое поле для ввода номеров телефонов.
TYPE_CLASS_TEXT
Текстовое поле, в которое можно ввести любые поддерживаемые символы.

Более подробная информация об этих константах приведена в справочной документации по InputType.

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

TYPE_TEXT_VARIATION_PASSWORD
Вариант TYPE_CLASS_TEXT для ввода паролей. Вместо текста в методе ввода отображаются символы.
TYPE_TEXT_VARIATION_URI
Вариант тега TYPE_CLASS_TEXT, предназначенный для ввода URL и других унифицированных идентификаторов ресурсов (URI).
TYPE_TEXT_FLAG_AUTO_COMPLETE
Вариант TYPE_CLASS_TEXT для ввода текста, который приложение автоматически дополняет из словаря, результатов поиска или других источников.

При тестировании этих вариантов маску inputType нужно заменить подходящей константой. Список доступных констант маски приведен в справочной документации по InputType.

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

Когда пользователь вводит текст с помощью вашего метода ввода, вы можете отправить текст в приложение, отправив отдельные события нажатия клавиш или отредактировав текст вокруг курсора в текстовом поле приложения. В любом случае для передачи текста используйте экземпляр InputConnection. Чтобы получить этот экземпляр, вызовите InputMethodService.getCurrentInputConnection().

Редактирование текста вокруг курсора

При редактировании существующего текста в BaseInputConnection могут быть полезны следующие методы:

getTextBeforeCursor()
Возвращает CharSequence с количеством запрошенных символов до текущей позиции курсора.
getTextAfterCursor()
Возвращает CharSequence, содержащий количество запрошенных символов после текущей позиции курсора.
deleteSurroundingText()
Удаляет указанное количество символов до и после текущего положения курсора.
commitText()
Вставляет CharSequence в текстовое поле и устанавливает новую позицию курсора.

Например, в следующем фрагменте кода показано, как заменить четыре символа слева от курсора текстом "Hello!":

Kotlin

currentInputConnection.also { ic: InputConnection ->
    ic.deleteSurroundingText(4, 0)
    ic.commitText("Hello", 1)
    ic.commitText("!", 1)
}

Java

InputConnection ic = getCurrentInputConnection();
ic.deleteSurroundingText(4, 0);
ic.commitText("Hello", 1);
ic.commitText("!", 1);

Поддержка составления текста перед отправкой

Если ваш метод ввода текста предлагает варианты или требует нескольких шагов для создания глифа или слова, вы можете показывать прогресс в текстовом поле, пока пользователь не подтвердит слово, а затем заменить частичный текст на полный. Вы можете выделить текст, добавив к нему тег span при передаче в setComposingText().

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

Kotlin

currentInputConnection.also { ic: InputConnection ->
    ic.setComposingText("Composi", 1)
    ic.setComposingText("Composin", 1)
    ic.commitText("Composing ", 1)
}

Java

InputConnection ic = getCurrentInputConnection();
ic.setComposingText("Composi", 1);
ic.setComposingText("Composin", 1);
ic.commitText("Composing ", 1);

Перехват ключевых событий оборудования

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

Чтобы перехватывать нажатия аппаратных клавиш, переопределите onKeyDown() и onKeyUp().

Вызовите метод super() для клавиш, которые вы не хотите обрабатывать самостоятельно.

Как создать подтип метода ввода

Подтипы позволяют IME поддерживать несколько режимов ввода и языков. Подтип может представлять:

  • Региональные настройки, например en_US или fr_FR.
  • Способ ввода, например голосовой, с клавиатуры или рукописный.
  • Другие стили ввода, формы или свойства, относящиеся к IME, например раскладки клавиатуры 10-key или QWERTY.

Режим может быть любым текстом, например "клавиатура" или "голос". Подтип может представлять собой комбинацию этих элементов.

Информация о подтипе используется в диалоговом окне переключения IME, доступном на панели уведомлений, и в настройках IME. Кроме того, эта информация позволяет фреймворку напрямую вызывать определенный подтип IME. При создании IME используйте функцию подтипа, поскольку она помогает пользователю идентифицировать и переключаться между разными языками и режимами IME.

Определите подтипы в одном из XML-файлов ресурсов метода ввода, используя элемент <subtype>. В приведенном ниже фрагменте кода определен метод ввода с двумя подтипами: подтипом клавиатуры для английского языка (США) и подтипом клавиатуры для французского языка (Франция):

<input-method xmlns:android="http://schemas.android.com/apk/res/android"
        android:settingsActivity="com.example.softkeyboard.Settings"
  @      android:icon>=&quo<t;drawable/ime_icon&qu@ot;
    subtype android:name="string/display_name_english_keybo@ard_ime"
            android:icon="drawable/subtype_icon_english_keyboard_ime"
            android:languageTag="en-US"
            android:imeSubtypeMode="keyboard&quo>t;
  <          android:imeS@ubtypeExtraValue="somePrivateOption=true" /
    subtype a@ndroid:name="string/display_name_french_keyboard_ime"
            android:icon="drawable/subtype_icon_french_keyboard_ime"
            android:languageTag="fr-FR"
            android:i>meSub<typeMode="keyboar@d"
            android:imeSubtypeExtraVal>u<e="someV>ariable=30,someInternalOption=false" /
    subtype android:name="string/display_name_german_keyboard_ime" ... /
/input-method

Чтобы подтипы были правильно обозначены в интерфейсе, используйте `%s`, чтобы получить ярлык подтипа, совпадающий с ярлыком подтипа в локали. Это показано в двух фрагментах кода ниже. Первый фрагмент содержит часть XML-файла способа ввода:

<subtype
    android:label=&@quot;string/label_subtype_generic"
    android:imeSubtypeLocale="en_US"@;
    android:icon="drawable/icon_en_us"
    andro>id:imeSubtypeMode="keyboard" /

Следующий фрагмент является частью файла strings.xml IME. Строковый ресурс label_subtype_generic, который используется в определении интерфейса метода ввода для установки ярлыка подтипа, определяется следующим образом:

<string name="label_subtype_gen>er<ic">;%s/string

Эта настройка приводит к тому, что отображаемое название подтипа соответствует настройкам языка. Например, для любого региона с английским языком отображаемое название будет "Английский (США)".

Как выбрать подтипы метода ввода на панели уведомлений

Система Android управляет всеми подтипами, которые предоставляет каждый метод ввода. Подтипы IME рассматриваются как режимы IME, к которым они относятся. Пользователь может перейти к меню доступных подтипов IME из панели уведомлений или приложения "Настройки", как показано на рисунке ниже.

Изображение, на котором показано системное меню &quot;Языки и ввод&quot;
Рисунок 4. Системное меню Язык и ввод.

Как выбрать подтипы IME в системных настройках

Пользователь также может управлять тем, как подтипы используются на панели настроек Язык и ввод в системных настройках:

Изображение меню выбора языков
Рисунок 5. Системное меню Языки

Как переключаться между подтипами IME

Вы можете разрешить пользователям легко переключаться между подтипами IME, предоставив им клавишу переключения, например значок языка в виде глобуса на клавиатуре. Это повышает удобство использования клавиатуры. Чтобы включить переключение, выполните следующие действия:

  1. Объявите supportsSwitchingToNextInputMethod = "true" в XML-файлах ресурсов метода ввода. Ваше объявление должно выглядеть примерно как следующий фрагмент кода:
    <input-method xmlns:android="http://schemas.android.com/apk/res/android"
            android:settingsActivity="com.example.softkeyboard.Settings"
      @      android:icon="drawable/ime_icon"
            android:supportsSwi>tchingToNextInputMethod="true"
  2. Вызовите метод shouldOfferSwitchingToNextInputMethod().
  3. Если метод возвращает значение true, покажите ключ переключения.
  4. Когда пользователь нажимает клавишу переключения, вызывается функция switchToNextInputMethod() с параметром false. Если задано значение false, система будет считать все подтипы одинаковыми, независимо от того, к какому методу ввода они относятся. Если указано значение true, система должна переключаться между подтипами в текущем IME.

Общие рекомендации по использованию IME

Вот ещё несколько рекомендаций по реализации метода ввода:

  • Предоставьте пользователям возможность задавать параметры непосредственно в интерфейсе IME.
  • Предоставьте пользователям возможность переключаться на другой метод ввода непосредственно из интерфейса метода ввода, поскольку на устройстве может быть установлено несколько методов ввода.
  • Быстро открывать интерфейс IME. Предварительно загружайте или загружайте по запросу большие ресурсы, чтобы пользователи видели IME сразу после нажатия на текстовое поле. Кэшировать ресурсы и представления для последующих вызовов метода ввода.
  • Освобождайте большие объемы памяти сразу после того, как окно метода ввода скрыто, чтобы у приложений было достаточно памяти для работы. Используйте отложенное сообщение, чтобы освободить ресурсы, если IME скрыт в течение нескольких секунд.
  • Убедитесь, что пользователи могут ввести как можно больше символов для языка или региона, связанных с IME. Пользователи могут использовать знаки препинания в паролях или именах пользователей, поэтому ваш метод ввода должен поддерживать множество разных символов, чтобы пользователи могли ввести пароль и получить доступ к устройству.