原文标题:Create A Custom Keyboard on Android
原文链接:http://code.tutsplus.com/tutorials/create-a-custom-keyboard-on-android–cms-22615
大部分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。
软键盘被Android系统看作是Input Method Editor (IME),IME在AndroidManifest.xml中被定为为一个Service
,并且
需要使用BIND_INPUT_METHOD
权限,并且添加android.view.InputMethod
action。
在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>
在上面的配置文件中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>
我们需要三个string,如下
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
属性的值是97,keyLabel
属性的值就是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
类有三个成员变量,
Keyboard
分配到KeyboardView
的实例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值,基于这个值,键盘可以执行以下动画,
KEYCODE_DELETE
,使用deleteSurroundingText
方法删除光标 KEYCODE_DONE
,KEYCODE_ENTER
事件会被发送。KEYCODE_SHIFT
,boolean类型的caps的值会被改变,并且使用setShifted
方法改变键盘的换档状态(shift state)
,当状态改变时,键盘需要重绘,所以的键的label
被更新了,invalidateAllKeys
方法用来重绘所有的键。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里,要使用它,我们需要在设置里激活它
激活Simple IME后,打开任意一个有输入框的应用(例如,消息),点击输入框,在通知栏你会看到输入法的图片,依赖你的设备,你可以点击图标或者拉下通知栏选择Simple IME作为输入法,现在你可以用你的新输入法输入内容啦。