Android 总结:自定义键盘实现原理和三种实例详解


一、实现原理

实现软键盘主要用到了系统的两个类 Keyboard 和 KeyboardView .

1. Keyboard

用于监听虚拟键盘:

  • Loads an XML description of a keyboard and stores the attributes of the keys. A keyboard consists of rows of keys . The layout file for a keyboard contains XML that looks like the following snippet :
"%10p"
         android:keyHeight="50px"
         android:horizontalGap="2px"
         android:verticalGap="2px" >
     "32px" >
         "A" />
         ...
     
     ...
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11

我们自定义的每一个按键都将会有一个 codes 值,比如回退我们就写成:

<Key android:codes="-5" 
     android:keyIcon="@drawable/sym_keyboard_delete"/>
  • 1
  • 2
  • 1
  • 2

再监听就是:

if (primaryCode == Keyboard.KEYCODE_DELETE){}
  • 1
  • 1

这就表示,监听 delete 事件了! 
定义  标签时常用到的 codes / KEYCODE 有:

    public static final int KEYCODE_SHIFT = -1;
    public static final int KEYCODE_MODE_CHANGE = -2;
    public static final int KEYCODE_CANCEL = -3;
    public static final int KEYCODE_DONE = -4;
    public static final int KEYCODE_DELETE = -5;
    public static final int KEYCODE_ALT = -6;
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

2. KeyboardView

处理绘制,检测按键,触摸动作:

  • A view that renders a virtual Keyboard. It handles rendering of keys and detecting key presses and touch movements.

我们会在Activity的xml文件中这样定义:

"@+id/keyboard_view"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_alignParentBottom="true"
            android:background="@color/gray"
            android:focusable="true"
            android:focusableInTouchMode="true"
            android:keyBackground="@drawable/bg_keyboard_selector"
            android:keyTextColor="@color/keyTextColor"
            android:visibility="gone"
            android:keyPreviewLayout="@layout/key_preview_layout"
            />
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 其中 android:keyPreviewLayout 指的是每当我们点击键盘上的某个键时,短暂弹出的布局。
属性名 描述
android:keyBackground Image for the key.
android:keyPreviewLayout Layout resource for key press feedback.
android:keyPreviewOffset Vertical offset of the key press feedback from the key.
android:keyTextColor Color to use for the label in a key.
android:keyTextSize Size of the text for character keys.
android:labelTextSize Size of the text for custom keys with some text and no icon.
android:popupLayout Layout resource for popup keyboards.
android:verticalCorrection Amount to offset the touch Y coordinate by, for bias correction.
  • 我们需要建一个xml文件,来布局我们的视图, 
    一般是在res文件夹中建一个名为xml的文件夹,在里面新建立一个xml布局文件。

  • 每一个按键的属性主要包括 
    Android:codes=" " 和 android:keyLabel="" 
    Activity 就是根据 codes 的值来监听的。 
    一些可以自定义设置,一些需要是 keyboard 中设置好的,要保持一致。

二、实例解析:在xml文件中定义

1. 效果图:

Android 总结:自定义键盘实现原理和三种实例详解_第1张图片 
建一个类,用于处理软键盘事件,文件名为 KeyboardUtil.Java

2. 定义键盘的键

键盘上键的细节和它的位置我们指定在一个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 .

有些键的code是负数,负数等于在Keyboard类 
中预定义的常量,例如,-5等于Keyboard.KEYCODE_DELETE

3. 定义按键声音

创建一个方法,这个方法的作用就是当我们按下某个键时发出一个声音,我们使用 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);
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17

4. 处理按键输入

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

  • 如果code是 KEYCODE_DELETE,使用deleteSurroundingText()方法删除光标左边的字符。
  • 如果code是 KEYCODE_DONE , KEYCODE_ENTER 事件会被发送。
  • 如果code是 KEYCODE_SHIFT,boolean类型的caps的值会被改变,并且使用 setShifted() 方法改变键盘的换档状态(shift state),当状态改变时,键盘需要重绘,所以的键的label被更新了,invalidateAllKeys() 方法用来重绘所有的键。
  • 对于 其他所有的codes,只是 简单的将unicode转化为字符并且发送到输入框里,如果这个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);
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24

三、实例解析:继承EditText自定义view来实现

保证不遮挡输入框,界面漂亮有动画。数字键盘可随机,支持纯色绘制按键。

1. 效果图

Android 总结:自定义键盘实现原理和三种实例详解_第2张图片 
这个程序有点Bug,点击输入法最底部的三个键时,虚拟按键也触发到了。 
想解这个Bug的话自行再仔细研究下代码。

1. 功能特点

  1. 在输入时,保证键盘不会挡住输入框。
  2. 可以直接通过 Edittext 的属性来配置要显示的键盘布局 xml 文件。点击 Edittext 输入框即可弹出键盘。
  3. 支持属性配置 已经写好的键盘 xml 。支持属性配置 是否随机数字。
  4. 支持纯色绘制按键。默认的 keyboardview 在android2.3等低版本下不能正常显示纯色的按键背景。本示范工程通过重写原有的 keyboardview 来解决这个bug。

注:资源里面只有两种键盘样式:数字0-9+"000"0-9+. 。你可以通过自己写键盘布局xml,然后通过属性引用来显示自己的键盘。具体可以参考附件的示范工程。

2. 逻辑分析

1) 生成随机数的key

先for循环生成0-9的KeyModel,存储到LinkedList中

    LinkedList tempList = new LinkedList();
    for (int i = 0; i < count; i++) {
        tempList.add(new KeyModel(48 + i, i + ""));
    }
  • 1
  • 2
  • 3
  • 4
  • 1
  • 2
  • 3
  • 4

后面通过随机数,从 LinkedList 中取出KeyModel放入新的 List 中(这里的代码是 resultList), 然后这里的 List 就是随机的了。

        Random rand = new SecureRandom();
        rand.setSeed(SystemClock.currentThreadTimeMillis());

        for (int i = 0; i < count; i++) {
            int num = rand.nextInt(count - i);
            // Log.d(TAG, " rand num"+num);
            KeyModel model = tempList.get(num);
            // Log.d(TAG, model.toString());
            resultList.add(new KeyModel(model.getCode(), model.getLabel()));
            tempList.remove(num);
        }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11

四、实例解析:以Dialog的形式来实现

1. 效果图

Android 总结:自定义键盘实现原理和三种实例详解_第3张图片

2. 技术分析略。

五、源码下载地址:

  1. 在xml文件中定义:customkeyboard 下载

  2. 继承EditText自定义view来实现:customkeyboardbyedittext 下载

  3. 以Dialog的形式来实现:customkeyboardbydialog 下载

上面三种“自定义键盘”方式并非严格区分。

参考文章: 
1. Android创建自定义键盘(制作系统键盘而不只是应用的可以参考这个) 
2. Android自定义键盘示范工程,可直接在项目中使用

本博文转载自  http://blog.csdn.net/u014136472/article/details/50257245

本来想自己写的但是无意中发现这篇博客,感觉写的很好,所以就转载了。

如果需要对每一个例子的源码进行说明的话,可在评论出说明,我再自己写一些博文进行分析


你可能感兴趣的:(Android,应用开发)