原文标题: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。

编辑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">SimpleKeyboardstring>    <string name="simple_ime">Simple IMEstring>    <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的代码如下,

@Overridepublic 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的代码,
@Overridepublic 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作为输入法,现在你可以用你的新输入法输入内容啦。

更多相关文章

  1. 使用命令行创建Android模拟器
  2. ListView
  3. Create Hello Android
  4. Android之布局属性
  5. 【Android】Android(安卓)UI 开发_问答_2
  6. Android之ListView中的分割线
  7. TextView中ellipsize属性焦点异常处理
  8. Android控件属性大全
  9. Android:只读EditText内容可滚动(禁止输入法)的实现

随机推荐

  1. Android* 应用性能调试
  2. android五种布局特点
  3. android时序图 以及UML中时序图、流程图
  4. Android(安卓)解压APK文件
  5. 【原创】Proton在Android上的编译
  6. Android里的菜单
  7. Android中的Context详解
  8. EditText属性
  9. android 回到桌面
  10. Android(安卓)Studio动态自定义编译输出