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

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

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

В этой статье рассматриваются следующие темы:

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

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

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

Изображение, на котором показан жизненный цикл IME.
Рисунок 1. Жизненный цикл 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"
    android:label="@string/fast_input_label"
    android:permission="android.permission.BIND_INPUT_METHOD">
    <intent-filter>
        <action android:name="android.view.InputMethod" />
    </intent-filter>
    <meta-data android:name="android.view.im"
               android:resource="@xml/method" />
</service>

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

<!-- Optional: an activity for controlling the IME settings. -->
<activity android:name="FastInputIMESettings"
    android:label="@string/fast_input_settings">
    <intent-filter>
        <action android:name="android.intent.action.MAIN"/>
    </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 оставьте достаточно места для приложения, чтобы показать текстовое поле и связанный с ним контекст. IME должен занимать не более половины экрана. В полноэкранном режиме IME такой проблемы нет.

Обработка разных типов ввода

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

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

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

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

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

Когда поле ввода получает фокус и запускается 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, редактор метода ввода для латиницы отображает цифровую клавиатуру для полей этого типа.
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.

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

Когда пользователь вводит текст с помощью вашего IME, вы можете отправлять текст в приложение, отправляя отдельные события клавиш или редактируя текст вокруг курсора в текстовом поле приложения. В обоих случаях для передачи текста используется экземпляр 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);

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

Если ваш IME предсказывает текст или требует нескольких шагов для составления глифа или слова, вы можете показать прогресс в текстовом поле до тех пор, пока пользователь не зафиксирует слово, а затем вы сможете заменить частичное композиция с завершенным текстом. Вы можете по-особому оформить текст, добавив к нему 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 клавишами или QWERTY.

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

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

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

<input-method xmlns:android="http://schemas.android.com/apk/res/android"
        android:settingsActivity="com.example.softkeyboard.Settings"
        android:icon="@drawable/ime_icon">
    <subtype android:name="@string/display_name_english_keyboard_ime"
            android:icon="@drawable/subtype_icon_english_keyboard_ime"
            android:languageTag="en-US"
            android:imeSubtypeMode="keyboard"
            android:imeSubtypeExtraValue="somePrivateOption=true" />
    <subtype android:name="@string/display_name_french_keyboard_ime"
            android:icon="@drawable/subtype_icon_french_keyboard_ime"
            android:languageTag="fr-FR"
            android:imeSubtypeMode="keyboard"
            android:imeSubtypeExtraValue="someVariable=30,someInternalOption=false" />
    <subtype android:name="@string/display_name_german_keyboard_ime" ... />
</input-method>

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

<subtype
    android:label="@string/label_subtype_generic"
    android:imeSubtypeLocale="en_US"
    android:icon="@drawable/icon_en_us"
    android:imeSubtypeMode="keyboard" />

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

<string name="label_subtype_generic">%s</string>

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

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

Система 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:supportsSwitchingToNextInputMethod="true">
  2. Вызовите метод shouldOfferSwitchingToNextInputMethod().
  3. Если метод возвращает значение true, покажите клавишу переключения.
  4. Когда пользователь нажимает клавишу переключения, вызывайте switchToNextInputMethod(), передавая значение false. Значение false указывает системе, что все подтипы следует обрабатывать одинаково, независимо от того, к какому IME они относятся. Если указано значение true, система должна перебирать подтипы в текущем редакторе метода ввода.

Общие рекомендации по работе с IME

При реализации IME необходимо учитывать следующее:

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