Создание собственной Android клавиатуры
Главная » Создание собственной Android клавиатуры

Сегодня мы научимся делать не что то там, а самую настоящую клавиатуру со всем стандартным набором возможностей, которую при желании вы сможете даже использовать на своем Android устройстве вместо стандартной (ну представьте себе - печатать смс-ки на собственной клавиатуре, что может быть лучше:)). Если вы думаете, что урок будет очень сложным - то вы правы вы ошибаетесь. Оказывается, что  в стандартном функционале Android SDK Manager есть абсолютно все необходимое для создания полноценной клавиатуры без лишних страданий. Нам не нужно будет ни вырисовывать внешний вид клавиш, ни настраивать распознавание нажатий клавиш, ни мучиться с привязыванием нашей будущей клавиатуры к полям ввода. Все это уже по умолчанию есть в SDK Manager, нам только осталось научиться этим воспользоваться!

Начнем. Создаем новый проект, я назвал его Keyboard (как мы неоднократно договаривались - все названия на ваше усмотрение, только лишь бы вы сами в них не путались). На том шаге, когда нам предлагается создать какую либо Activity, выбираем Add No Activity и жмем финиш. 

Перво наперво, что нам нужно сделать это получить для нашего приложения разрешение ему быть клавиатурой:). Мы хотим создать свое устройство ввода, что называется Input Method Editor (IME), оно определяется в файле манифеста как Service, использующий разрешение BIND_INPUT_METHOD и соответствует действию android.view.InputMethod.  Идем в файл манифеста AndroidManifest.xml и добавляем в тег <application> следующее:

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

Видим красные подчеркивания (нам не привыкать), не волнуемся. 

Тег Service содержит тег <meta-data>, который ссылается на несуществующий пока файл method.xml. без этого файла ОС Android не признает нашу клавиатуру как корректный метол ввода, который можно использовать.  Этот файл должен содержать подробности о методе ввода и его подтипах. Для нашей клавиатуры определим всего один подтип для en_US локализации (не будем пока экспериментировать с русским, нам главное понять принцип). Создаем в приложении res/layout папку и создаем в ней нужный нам файл  method.xml. Вносим в файл res/layout/mehod.xml следующее:

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

Теперь  давайте отредактируем файл строковых ресурсов res/values/strings.xml. Нам нужно добавить сюда строки для:

     - имя приложения;

     - название для IME;

     - название для подтипа IME;

Имя приложения у нас уже есть, остается только добавить две нужные строчки. Отредактированный файл strings.xml выглядит так:

<resources>
 <string name="app_name">Keyboard</string>
 <string name="ime">IME</string>
 <string name="subtype_en_US">English (US)</string>
</resources>

Теперь нужно определить внешний вид клавиатуры. Файл layout для нашей клавиатуры будет содержать только один объект - KeyboardView.  Для того, чтобы наша клавиатура появлялась в низу экрана, настроим атрибут layout_alignParentBottom со значением true. Создаем файл res/layout/keyboard.xml следующее:

<?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:keyPreviewLayout ="@layout/preview"
 />

Строка keyPreviewLayout отвечает за то, как будет выглядеть нажатая клавиша на нашей клавиатуре. Как видите, за ее вид будет отвечать файл preview, который мы и создадим. Создаем файл res/layout/preview.xml и добавим в него следующее содержимое:

<?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="#ffff00"
 android:textStyle="bold"
 android:textSize="30sp"
 >
</TextView>

Теперь нужно определить клавиши клавиатуры. Подробное описание клавиш и их расположение описывается в специальном .xml файле.  Каждая клавиша имеет следующие атрибуты:
     - keyLabel - название клавиши, которое будет отображаться на клавише;

     - codes - этот атрибут содержит уникальный код, который характеризует нажатую клавишу.

Каждая клавиша, будь то цифра, буква, какой - либо знач имеют свой собственный уникальный код. Например, буква А имеет значение  codes - 97, соответственно keyLabel нужно придать значение "А". Одной клавише можно задать несколько значений в атрибут codes, то клавиша будет принимать определенное значение из этих троих а зависимости от того, сколько раз мы нажали на клавишу (вспомните клавиатурный набор на старых мобильных - на одной клавише "а,б,в,г" и т.д.). Например, если мы зададим кнопке коды 63,33 и 58 то при одном нажатии получим символ "?", при двух нажатиях в короткой последовательности получим "!", а при троих нажатиях - двоеточие ":".

 Клавиша может иметь еще несколько атрибутов:

     - keyEdgeFlags - атрибут может применять значение left или right. Эти атрибуты добавляются клавишам, которые расположены в самом левом или самом правом положении, соответственно;

     - keyWidth - определяет ширину клавиши. Как правило задается в процентах;

     - isRepeatable - если этот атрибут имеет значение true, то долгое нажатие на клавишу будет повторять ее действие несколько раз (например "аааааа"), как правило эту опцию включают для клавиш удаления, пробела и т.п. 

Клавиши в клавиатуре сгруппированы в колонки. Довольно практично делать по 10 клавиш в колонке и каждой клавише отдавать 10% ширины от общей ширины клавиатуры. Мы выставим высоту клавиш равную 60dp. Можно ее изменять, но не рекомендуется делать высоту меньше 48dp. У нас будет 5 столбцов клавиш.

Переходим от теории к действию. Создаем файл res/layout/qwerty.xml. Файл должен содержать следующий код:
 

<Keyboard xmlns:android="http://schemas.android.com/apk/res/android"
 android:keyWidth="10%p"
 android:horizontalGap="0px"
 android:verticalGap="0px"
 android:keyHeight="60dp"
 android:layout_height="wrap_content"
 android:layout_width="wrap_content">
 <Row>
 <Key android:codes="49" android:keyLabel="1" android:keyEdgeFlags="left"/>
 <Key android:codes="50" android:keyLabel="2"/>
 <Key android:codes="51" android:keyLabel="3"/>
 <Key android:codes="52" android:keyLabel="4"/>
 <Key android:codes="53" android:keyLabel="5"/>
 <Key android:codes="54" android:keyLabel="6"/>
 <Key android:codes="55" android:keyLabel="7"/>
 <Key android:codes="56" android:keyLabel="8"/>
 <Key android:codes="57" android:keyLabel="9"/>
 <Key android:codes="48" android:keyLabel="0" android:keyEdgeFlags="right"/>
 </Row>
 <Row>
 <Key android:codes="113" android:keyLabel="q" android:keyEdgeFlags="left"/>
 <Key android:codes="119" android:keyLabel="w"/>
 <Key android:codes="101" android:keyLabel="e"/>
 <Key android:codes="114" android:keyLabel="r"/>
 <Key android:codes="116" android:keyLabel="t"/>
 <Key android:codes="121" android:keyLabel="y"/>
 <Key android:codes="117" android:keyLabel="u"/>
 <Key android:codes="105" android:keyLabel="i"/>
 <Key android:codes="111" android:keyLabel="o"/>
 <Key android:codes="112" android:keyLabel="p" android:keyEdgeFlags="right"/>
 </Row>
 <Row>
 <Key android:codes="97" android:keyLabel="a" android:keyEdgeFlags="left"/>
 <Key android:codes="115" android:keyLabel="s"/>
 <Key android:codes="100" android:keyLabel="d"/>
 <Key android:codes="102" android:keyLabel="f"/>
 <Key android:codes="103" android:keyLabel="g"/>
 <Key android:codes="104" android:keyLabel="h"/>
 <Key android:codes="106" android:keyLabel="j"/>
 <Key android:codes="107" android:keyLabel="k"/>
 <Key android:codes="108" android:keyLabel="l"/>
 <Key android:codes="35,64" android:keyLabel="\# \@" android:keyEdgeFlags="right"/>
 </Row>
 <Row>
 <Key android:codes="-1" android:keyLabel="CAPS" android:keyEdgeFlags="left"/>
 <Key android:codes="122" android:keyLabel="z"/>
 <Key android:codes="120" android:keyLabel="x"/>
 <Key android:codes="99" android:keyLabel="c"/>
 <Key android:codes="118" android:keyLabel="v"/>
 <Key android:codes="98" android:keyLabel="b"/>
 <Key android:codes="110" android:keyLabel="n"/>
 <Key android:codes="109" android:keyLabel="m"/>
 <Key android:codes="46" android:keyLabel="."/>
 <Key android:codes="63,33,58" android:keyLabel="\? ! :" android:keyEdgeFlags="right"/>
 </Row>
 <Row android:rowEdgeFlags="bottom">
 <Key android:codes="44" android:keyLabel="," android:keyWidth="10%p" android:keyEdgeFlags="left"/>
 <Key android:codes="47" android:keyLabel="/" android:keyWidth="10%p" />
 <Key android:codes="32" android:keyLabel="SPACE" android:keyWidth="40%p" android:isRepeatable="true"/>
 <Key android:codes="-5" android:keyLabel="DEL" android:keyWidth="20%p" android:isRepeatable="true"/>
 <Key android:codes="-4" android:keyLabel="DONE" android:keyWidth="20%p" android:keyEdgeFlags="right"/>
 </Row>
</Keyboard>

Некоторые клавиши могут иметь отрицательное значение атрибута codes, они эквиваленты встроенным константам в класс Keyboard. Например, значение -5 соответствует значению Keyboard.KEYCODE_DELETE

Теперь создадим новый Java класс и назовем его IME.java. Он должен наследовать класс InputMethodService и выполнять интерфейс OnKeyboardActionListener. Этот интерфейс содержит методы, которые вызываются при нажатии на клавиши. 

Класс IME должен иметь 3 типа переменных:

     - KeyboardView - ссылающийся на вид, определенный в layout файле;

     - Keyboard - применяет то, что было назначено в KeyboardView;

     - boolean - сообщает о включенном caps lock-е. 

Если проделать все сказанное, наш файл IME.java приобретает вид:

package home.keyboard;

import android.inputmethodservice.InputMethodService;
import android.inputmethodservice.Keyboard;
import android.inputmethodservice.KeyboardView;

public class IME extends InputMethodService implements KeyboardView.OnKeyboardActionListener{

 private KeyboardView kv;
 private Keyboard keyboard;

 private boolean caps = false;

 @Override
 public void onPress(int primaryCode) {
 }
 @Override
 public void onRelease(int primaryCode) {
 }
 @Override
 public void onKey(int primaryCode, int[] keyCodes) {
 }
 @Override
 public void onText(CharSequence text) {
 }
 @Override
 public void swipeLeft() {
 }
 @Override
 public void swipeRight() {
 }
 @Override
 public void swipeDown() {
 }
 @Override
 public void swipeUp() {
 }
}

Когда клавиатура создана, вызывается метод onCreateInputView. Все переменные тега Service можно инициализировать прямо здесь в методе. Добавьте метод onCreateInputView:

 @Override
 public View onCreateInputView() {
 kv = (KeyboardView)getLayoutInflater().inflate(R.layout.keyboard, null);
 keyboard = new Keyboard(this, android.R.layout.qwerty);
 kv.setKeyboard(keyboard);
 kv.setOnKeyboardActionListener(this);
 return kv;
 }

Теперь нужно настроить проигрывание звука при нажатии на клавиши. Используем для этого класс AudioManager. Используем стандартные звуки в методе PlayClick. Код блока настройки звука:

private void playClick(int keyCode){
 AudioManager am = (AudioManager)getSystemService(AUDIO_SERVICE);
 switch(keyCode){
 case 32:
 am.playSoundEffect(AudioManager.FX_KEYPRESS_SPACEBAR);
 break;
 case Keyboard.KEYCODE_DONE:
 case 10:
 am.playSoundEffect(AudioManager.FX_KEYPRESS_RETURN);
 break;
 case Keyboard.KEYCODE_DELETE:
 am.playSoundEffect(AudioManager.FX_KEYPRESS_DELETE);
 break;
 default: am.playSoundEffect(AudioManager.FX_KEYPRESS_STANDARD);
 }
 }

Теперь нам нужно настроить метод onKey для того, чтобы заставить нашу клавиатуру работать с полями ввода (как правило EditText) других приложений. Для этого используется метод getCurrenrInputConection. Здесь нужно настроить следующее:

     - commitText - для добавления одного или больше символов в поле ввода;

     - deleteSurroundingText - для удаления одного или более символов в поле ввода;

     - sendKeyEvent - отослать событие, типа KEYCODE_ENTER, во внешнее приложение (любое где мы будем использовать нашу клавиатуру).

Каждый раз когда пользователь нажимает на клавишу на клавиатуре вызывается метод onKey, передавая уникальное значение (codes рассмотрено выше) нажатой клавиши. Учитывая значение принятого кода, клавиатура выполняет определенное действие, которое соответствует коду.

Приведите свой метод onKey к такому виду:

@Override
 public void onKey(int primaryCode, int[] keyCodes) {
 InputConnection ic = getCurrentInputConnection();
 playClick(primaryCode);
 switch(primaryCode){
 case Keyboard.KEYCODE_DELETE :
 ic.deleteSurroundingText(1, 0);
 break;
 case Keyboard.KEYCODE_SHIFT:
 caps = !caps;
 keyboard.setShifted(caps);
 kv.invalidateAllKeys();
 break;
 case Keyboard.KEYCODE_DONE:
 ic.sendKeyEvent(new KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_ENTER));
 break;
 default:
 char code = (char)primaryCode;
 if(Character.isLetter(code) && caps){
 code = Character.toUpperCase(code);
 }
 ic.commitText(String.valueOf(code),1);
 }
 }

И вот наконец то все! Мы добили наш урок и клавиатура готова к тестированию. Стоит отметить, что так, как наша клавиатура не имеет никакого Activity (Add No Activity, помните?) то ее после установки на эмулятор или устройство не отобразит среди установленных программ. Все дружно идем в настройки --- настройки ввода и клавиатуры и там активируем наше творение, как используемый (или один из используемых) метод ввода. Запускаем что либо, где нужно вводить текст, и тестим!

Оригинал этого руководства здесь.

Категория: Уроки программирования | Просмотров: 2680 | Добавил: Oleg | Теги: программируем клавиатуру, создание клавиатуры, Android, клавиатура | Рейтинг: 0.0/0
Всего комментариев: 3
avatar
1 Smytpf • 23:23, 30.08.2015
А как сделать переключение между языками??)
avatar
2 Smytpf • 23:26, 30.08.2015
Напишите в вк мне или сделайте урок по моему вопросу)
avatar
3 TheEvil • 22:59, 15.02.2016
Здравствуйте, а не подскажете, каким образом реализовать кнопку переключения на дополнительный раздел (как с символами [ ] { } $ % # и т.д.) с часто используемыми функциями и модулями типа print import if elif while True и т.д. и можно ли заставить при нажатии на кнопку к примеру с функцией print сразу прописывать скобки и кавычки, при этом устанавливать курсор между кавычек ( пример print("") ) (или установка курсора реализуется исключительно в IDE?)?
avatar