Здравствуйте. В данной статье я постараюсь рассказать и показать основные моменты написания собственной клавиатуры для Android'а. Статья предназначена для разработчиков, которые с этим не сталкивались, но имеют опыт знакомства с Android'ом.

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

Для начала необходимо создать пустой Android проект без Activity. После этого приступим к подготовке .xml файлов, которые будут описывать Android'у нашу клавиатуру.

Базовый layout-файл, keyboard.xml

Содержит в себе View класс Android'а под названием KeyboardView и описывает внешний вид клавиатуры.

<?xml version="1.0" encoding="utf-8"?>
<android.inputmethodservice.KeyboardView
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/keyboard"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:layout_alignParentBottom="true"
    android:keyPreviewHeight="35dp"
    android:keyPreviewLayout="@layout/preview" />

Атрибуты:

  • android: keyPreviewHeight — задает высоту элемента подсказки, на котором отображается текущая нажатая клавиша.
  • android:keyPreviewLayout — указываем layout-файл, который описывает внешний вид preview'шки.

Код preview:

<?xml version="1.0" encoding="utf-8"?>
<TextView xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:gravity="center"
    android:background="@color/key_preview_background"
    android:textColor="@color/key_preview_text_color"
    android:textStyle="bold"
    android:textSize="25sp" />

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

Описание раскладки

Итак, мы подготовили 2 .xml файла, которые описывают внешний вид, теперь настал черед описать саму раскладку клавиатуры. Назовем этот файл keys_definition_ru.xml и находится он будет в xml ресурсах проекта. Здесь будет представлен лишь его кусок, так как файл достаточно большой.

<?xml version="1.0" encoding="utf-8"?>
<Keyboard xmlns:android="http://schemas.android.com/apk/res/android"
    android:keyWidth="7.5%p"
    android:horizontalGap="5px"
    android:verticalGap="0px"
    android:keyHeight="40dp">

    <Row>
        <Key android:codes="-1" android:keyIcon="@drawable/ic_keyboard_capslock_white_24dp"
            android:keyWidth="13%p" android:keyEdgeFlags="left" />
        <Key android:codes="1103" android:keyLabel="я"  />
        <Key android:codes="1095" android:keyLabel="ч" />
        <Key android:codes="1089" android:keyLabel="с" />
        <Key android:codes="1084" android:keyLabel="м" />
        <Key android:codes="1080" android:keyLabel="и" />
        <Key android:codes="1090" android:keyLabel="т" />
        <Key android:codes="1100" android:keyLabel="ь" />
        <Key android:codes="1073" android:keyLabel="б" />
        <Key android:codes="1102" android:keyLabel="ю" />
        <Key android:codes="-5" android:keyIcon="@drawable/ic_backspace_white_24dp"
            android:isRepeatable="true" android:keyEdgeFlags="right" android:keyWidth="13%p" />
    </Row>
</Keyboard>

Атрибуты:

Все атрибуты описывать не будем, лишь «не очевидные».

  • android:horizontalGap — горизонтальный отступ между клавишами
  • android: verticalGap — вертикальный отступ
  • android:codes — код нужного символа в html utf-8 (и не только utf-8, подробнее в оф. документации)
  • android:keyEdgeFlags — атрибут может применять значение left или right. Эти атрибуты добавляются клавишам, которые расположены в самом левом крае или самом правом крае клавиатуры
  • android:isRepeatable — повторять действие клавиши при долгом нажатии (обычно используется на пробеле или backspace)

Заключительный файл — описание локализаций (подтипов инпута):

<?xml version="1.0" encoding="utf-8"?>
<input-method xmlns:android="http://schemas.android.com/apk/res/android">
    <subtype
        android:label="@string/subtype_en_US"
        android:imeSubtypeLocale="en_US"
        android:imeSubtypeMode="keyboard" />
    <subtype
        android:label="@string/subtype_ru_RU"
        android:imeSubtypeLocale="ru_RU"
        android:imeSubtypeMode="keyboard" />
</input-method>

InputMethodService — сервис клавиатуры

Теперь, после того как мы создали все необходимые xml файлы, приступаем к описанию сервиса, который будет слушать события InputMethod.

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

Методы onCreateInputView и onKey
    @Override
    public View onCreateInputView() {
        mKeyboardView = (KeyboardView) getLayoutInflater().inflate(R.layout.keyboard, null);
        mKeyboard = new Keyboard(this, R.xml.keys_definition_ru);
        mKeyboard.setShifted(isCapsOn);  //приводим клавиатуру к верхнему регистру, если шифт нажат включен
        mKeyboardView.setKeyboard(mKeyboard);
        mKeyboardView.setOnKeyboardActionListener(this);

        return mKeyboardView;
    }

    @Override
    public void onKey(int primaryCode, int[] ints) {
        Log.d(TAG, "onKey " + primaryCode);
        InputConnection ic = getCurrentInputConnection();
        playClick(primaryCode);

        switch (primaryCode) {
            case Keyboard.KEYCODE_DELETE:
                ic.deleteSurroundingText(1, 0);
                break;
            case Keyboard.KEYCODE_SHIFT:
                handleShift();
                break;
            case Keyboard.KEYCODE_DONE:
                ic.sendKeyEvent(new KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_ENTER));
                break;
            case Keyboard.KEYCODE_ALT:
                handleSymbolsSwitch();
                break;
            case Keyboard.KEYCODE_MODE_CHANGE:
                handleLanguageSwitch();
                break;
            default:
                char code = (char) primaryCode;
                if (Character.isLetter(code) && isCapsOn) {
                    code = Character.toUpperCase(code);
                }

                ic.commitText(String.valueOf(code), 1);
                break;
        }
    }


Одним из методов жизненного цикла InputMethodService является onCreateInputView внутри которого мы создаем View клавиатуры и привязываем к ней необходимые листенеры.

Событие onKey срабатывает между onPress и onRelease, на вход им подается код нажатой клавиши.

Итак, все готово… почти, осталось добавить наш сервис в манифест.

<service android:name=".SimpleIME"
            android:label="@string/simple_ime"
            android:permission="android.permission.BIND_INPUT_METHOD">
            <meta-data android:name="android.view.im" android:resource="@xml/method" />
            <intent-filter>
                <action android:name="android.view.InputMethod" />
            </intent-filter>
        </service>

Поздравляю, вы написали свою первую клавиатуру!

> Исходный код клавиатуры (по умолчанию в ней включен капс)
> Официальная документация / туториал
Поделиться с друзьями
-->

Комментарии (7)


  1. dajver
    17.02.2017 17:43
    +1

    Спасибо за статью. А как сделать детект набора свайпом? Как прикрутить голосовой ввод? Есть какие-то примеры с этими функциями? А то у вас уж очень просто все :)


    1. Mr_NoName
      17.02.2017 18:52

      Все, что я вам сейчас скажу, будет лишь моими предположениями, так как сам я этого еще не реализовывал.
      Свайп, так как клавиатура это обыкновенная View, то вы можете навесить на нее листенеры того же onTouch, и написать нужную логику.
      Голосовой ввод, можно добавить кнопку, по нажатию на которую открывается диалоговое окно с распознаванием речи, например голосовой поиск — https://developer.android.com/guide/topics/search/search-dialog.html
      Возможно, позже напишу и как эти моменты можно сделать, как время свободное появится.


  1. Zulusho
    17.02.2017 22:14

    Спасибо за статью!
    Интересует вопрос — а есть какие-нибудь способы клавиатуре понять в каком приложении она была запущена?


    1. Mr_NoName
      18.02.2017 02:12

      Да, можно, вернее один способ точно есть, так как клавиатура — это в первую очередь сервис, то можно воспользоваться этим. Сомневаюсь, что есть еще способ, так как единственное связующее звено между клавиатурой и полем ввода — это getCurrentInputMethod, а от него вы максимум сможете получить Handler и то только с 23-й версии API.


  1. ivan386
    18.02.2017 16:08

    Интересно можно ли подредактировать стандартную клавиатуру Samsung? Давно хочу её сделать полупрозрачной.


    1. STFBEE
      18.02.2017 21:20

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


      1. ivan386
        18.02.2017 22:26

        Я включил плавающую клавиатуру на планшете чтоб она не уменьшала площадь приложения и не дёргала его. Она компактная но приходится её двигать если она перекрывает нужную часть приложения.