Android创建自定义键盘

原文标题:Create A Custom Keyboard on Android

原文链接:http://code.tutsplus.com/tutorials/create-a-custom-keyboard-on-android–cms-22615

Android创建自定义键盘_第1张图片

大部分Android设备没有物理键盘,作为代替,这些设备使用软键盘来接受用户输入,如果你对Android的个性化开发感兴趣,相信,创建一个自定义的软键盘会带你到另外一个全新的高度。
利用Android SDK,你可以用很少的代码快速的创建一个软键盘,因为SDK负责了很多低水平的任务,例如识别键的touch事件、绘制键盘、在键盘和输入框之间建立联系。

预备知识

你需要eclipse ADT,不过建议使用Android studio[loader注]

创建一个新项目

创建一个新项目——SimpleKeyboard,保证你起了一个唯一的包名,minimum required SDK设置为Android 2.2,target SDK设置为Android 4.4。我们的项目没有activity,所以不需要选择Create Activity,点击finish。

编辑Manifest文件

软键盘被Android系统看作是Input Method Editor (IME),IME在AndroidManifest.xml中被定为为一个Service,并且
需要使用BIND_INPUT_METHOD权限,并且添加android.view.InputMethodaction。
在Application节点下添加一下代码,

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

创建method.xml

在上面的配置文件中service有一个meta-data引用了method.xml文件,没有这个文件,Android系统不会将我们的service视为一个有效的IME,这个文件包含了输入法和它的subtype的描述,对于我们的键盘,我们定义了一个单一的en_US英文键盘,如果你的项目中没有res/xml目录,创建它,并将method.xml添加到这个目录中,这个文件的内容如下,

<?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" />
</input-method>

编辑 strings.xml

我们需要三个string,如下

  • 我们APP的名称
  • 输入法的标签
  • 输入法子类的标签

strings.xml文件的内容如下,

<resources>
    <string name="app_name">SimpleKeyboard</string>
    <string name="simple_ime">Simple IME</string>
    <string name="subtype_en_US">English (US)</string>
</resources>

创建键盘布局

键盘的布局包含一个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指的是每当我们点击键盘上的某个键时,短暂弹出的布局。
创建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 这个属性是指这个键代表的字符的unicode

例如,我们定义了一个字母A,codes属性的值是97keyLabel
属性的值就是A。如果一个code对应多个key,这个key代表的字符取决于这个key接受到的点击数taps)。
例如,一个键具有63,33,58编码,

  • 一次点击就是?
  • 两次点击就是!
  • 三次点击就是:

一个key还可以有一些可选的属性,

  • keyEdgeFlags 这个属性的值可以是left或者right,这个属性
    通常加在一行中最左边和最右边的键上。
  • keyWidth 这个属性定义了键的宽度,通常是一个百分比的值。
  • isRepeatable 这个属性如果设置为true,那么当长按该键时就会
    重复接受到该键上的动作,在删除键键和空格键上通常设为true

键盘上的键都是按行分组,通常情况下我们每行上的键限制到10个以内,
并且每个键占整个键盘宽度的10%,我们将键的高度设置为60dp,这个值可以调整,但是不建议设置低于48dp,我们的键盘会有5行的键。

现在,我们开始定义键盘,创建res/xml/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" >
    <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>

你可能已经注意到了,有些键的code是负数,负数等于在Keyboard
中预定义的常量,例如,-5等于Keyboard.KEYCODE_DELETE

创建Service

创建一个新的java类,并命令为SimpleIME.java,这个类要继承自
InputMethodService类并且实现OnKeyboardActionListener接口,OnKeyboardActionListener接口中的方法是在键盘上的键被点击或者按下时调用。
SimpleIME类有三个成员变量,

  • KeyboardView 我们在layout中定义的view
  • Keyboard 分配到KeyboardView的实例
  • 一个boolean类型的值代表了caps lock是否可用

然后实现OnKeyboardActionListener中的方法,SimpleIME的代码如下,

public class SimpleIME extends InputMethodService implements OnKeyboardActionListener{

    private KeyboardView kv;
    private Keyboard keyboard;

    private boolean caps = false;

    @Override
    public void onKey(int primaryCode, int[] keyCodes) {

    }

    @Override
    public void onPress(int primaryCode) {
    }

    @Override
    public void onRelease(int primaryCode) {
    }

    @Override
    public void onText(CharSequence text) {
    }

    @Override
    public void swipeDown() {
    }

    @Override
    public void swipeLeft() {
    }

    @Override
    public void swipeRight() {
    }

    @Override
    public void swipeUp() {
    }
}

当键盘创建时,onCreateInputView方法会被调用,在这个方法中我们初始化那三个成员变量,onCreateInputView的代码如下,

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

接下来,我们创建一个方法,这个方法的作用就是当我们按下某个键时发出一个声音,我们使用AudioManager来播放这个声音,Android SDK给我们提供了一些键盘的声效,我们在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方法,以便我们的输入法可以和其他应用程序的输入框交互,getCurrentInputConnection方法可以获取我们输入法与其他应用程序的连接,当我们获取到了连接,我们可以使用以下方法,

  • commitText 可以往编辑框里添加一个或多个字符
  • deleteSurroundingText 可以删除编辑框里一个多个字符
  • sendKeyEvent 可以发送事件到其他应用程序,例如KEYCODE_ENTER

当用户按下键盘上的一个键时,onKey会被调用,并且发送这个键的unicode值,基于这个值,键盘可以执行以下动画,

  • 如果code是KEYCODE_DELETE,使用deleteSurroundingText方法删除光标
    左边的字符。
  • 如果code是KEYCODE_DONE,KEYCODE_ENTER事件会被发送。
  • 如果code是KEYCODE_SHIFT,boolean类型的caps的值会被改变,并且使用setShifted方法改变键盘的换档状态(shift state),当状态改变时,键盘需要重绘,所以的键的label被更新了,invalidateAllKeys方法用来重绘所有的键。
  • 对于其他所有的codes,只是简单的将uncode转化为字符并且发送到输入框里,如果这个code代表了字母表里的一个字母,并且caps变量为true,那么我们需要将字母转化为大写。
    修改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);
    }
}

测试键盘

我们的键盘现在可以测试了,将我们的程序运行到android设备上,这个应用没有activity,这意味着我们的应用不会出现在launcher里,要使用它,我们需要在设置里激活它

Android创建自定义键盘_第2张图片

激活Simple IME后,打开任意一个有输入框的应用(例如,消息),点击输入框,在通知栏你会看到输入法的图片,依赖你的设备,你可以点击图标或者拉下通知栏选择Simple IME作为输入法,现在你可以用你的新输入法输入内容啦。

Android创建自定义键盘_第3张图片

你可能感兴趣的:(keyboard)