Android 自定义软键盘

一、 Android 自定义软键盘开发流程
  1. 建立软键盘样式
           即在项目res文件夹中创建所需要的各种软键盘的样式(比如说:字母、数字、符号 等)。
  2. 创建layout布局文件(非必须)
           在布局文件中给软键盘创建container,以便显示软键盘
  3. 自定义KeyboardView
           自定义一个KeyboardView 并继承自KeyboardView,在自定义的KeyboardView中绘制特殊按键,包括按键的点击背景,图片,文字 等。
  4. 自定义一个普通java类,一般取名为 **Keyboard.java
           把软键盘加载到container中,即在布局文件里预留的存放软键盘 的container。
           在类的内部实现软键盘的输入控制,键盘转换控制,软键盘的显示与隐藏控制 等。
           在需要用到软键盘的Activity中实例化该Keyboard 类,并传入必要的数据和信息。
二、建立软键盘样式

       建立软键盘样式可以直接通过xml进行排版,在res/xml中创建一个根节点为Keyboard的xml就可以开始排版了
这个xml中属性如下:

Keyboard.Key 属性 介绍
android:codes 此键输出的unicode值或逗号分隔值。
android:horizontalGap 键之间的默认水平间隙。
android:iconPreview 弹出预览中显示的图标。
android:isModifier 这是否是修改键,如Alt或Shift。
android:isRepeatable 是否长按此键会使其重复。
android:isSticky 这是否是切换键。
android:keyEdgeFlags 关键边缘标志。
android:keyHeight 键的默认高度,以像素为单位或显示宽度的百分比。
android:keyIcon 要在键上显示的图标而不是标签。
android:keyLabel 要在键上显示的标签。
android:keyOutputText 按下此键时要输出的字符串。
android:keyWidth 键的默认宽度,以像素为单位或显示宽度的百分比。
android:popupCharacters 要在弹出键盘中显示的字符。
android:popupKeyboard 任何弹出键盘的XML键盘布局。

实例代码:
数字键盘

<?xml version="1.0" encoding="utf-8"?>
<Keyboard xmlns:android="http://schemas.android.com/apk/res/android"
    android:horizontalGap="2.5%p"
    android:keyHeight="6%p"
    android:keyWidth="30%p"
    android:verticalGap="10px">
    <Row>
        <Key android:codes="49" android:keyLabel="1" />
        <Key android:codes="50" android:keyLabel="2" />
        <Key android:codes="51" android:keyLabel="3" />
    </Row>
    <Row>
        <Key android:codes="52" android:keyLabel="4" />
        <Key android:codes="53" android:keyLabel="5" />
        <Key android:codes="54" android:keyLabel="6" />
    </Row>
    <Row>
        <Key android:codes="55" android:keyLabel="7" />
        <Key android:codes="56" android:keyLabel="8" />
        <Key android:codes="57" android:keyLabel="9" />
    </Row>
    <Row>
        <Key android:codes="-2" android:keyLabel="ABC" />
        <Key android:codes="48" android:keyLabel="0" />
        <Key android:codes="-35" android:isRepeatable="true" />
    </Row>
</Keyboard>

英文键盘:

<?xml version="1.0" encoding="utf-8"?>
<Keyboard xmlns:android="http://schemas.android.com/apk/res/android"
    android:horizontalGap="1%p"
    android:keyHeight="6%p"
    android:keyWidth="10%p"
    android:verticalGap="10px">
    <Row>
        <Key android:codes="113" android:keyEdgeFlags="left" android:keyLabel="q" android:keyWidth="8.9%p" />
        <Key android:codes="119" android:keyLabel="w" android:keyWidth="8.9%p" />
        <Key android:codes="101" android:keyLabel="e" android:keyWidth="8.9%p" />
        <Key android:codes="114" android:keyLabel="r" android:keyWidth="8.9%p" />
        <Key android:codes="116" android:keyLabel="t" android:keyWidth="8.9%p" />
        <Key android:codes="121" android:keyLabel="y" android:keyWidth="8.9%p" />
        <Key android:codes="117" android:keyLabel="u" android:keyWidth="8.9%p" />
        <Key android:codes="105" android:keyLabel="i" android:keyWidth="8.9%p" />
        <Key android:codes="111" android:keyLabel="o" android:keyWidth="8.9%p" />
        <Key android:codes="112" android:keyEdgeFlags="right" android:keyLabel="p" android:keyWidth="8.9%p" />
    </Row>
    <Row>
        <Key android:codes="97" android:horizontalGap="5.5%p" android:keyEdgeFlags="left" android:keyLabel="a" android:keyWidth="9%p" />
        <Key android:codes="115" android:keyLabel="s" android:keyWidth="9%p" />
        <Key android:codes="100" android:keyLabel="d" android:keyWidth="9%p" />
        <Key android:codes="102" android:keyLabel="f" android:keyWidth="9%p" />
        <Key android:codes="103" android:keyLabel="g" android:keyWidth="9%p" />
        <Key android:codes="104" android:keyLabel="h" android:keyWidth="9%p" />
        <Key android:codes="106" android:keyLabel="j" android:keyWidth="9%p" />
        <Key android:codes="107" android:keyLabel="k" android:keyWidth="9%p" />
        <Key android:codes="108" android:keyEdgeFlags="right" android:keyLabel="l" android:keyWidth="9%p" />
    </Row>
    <Row>
        <Key android:codes="-1" android:isModifier="true" android:isSticky="true" android:keyEdgeFlags="left" android:keyWidth="13%p" />
        <Key android:codes="122" android:horizontalGap="1.5%p" android:keyLabel="z" android:keyWidth="9%p" />
        <Key android:codes="120" android:keyLabel="x" android:keyWidth="9%p" />
        <Key android:codes="99" android:keyLabel="c" android:keyWidth="9%p" />
        <Key android:codes="118" android:keyLabel="v" android:keyWidth="9%p" />
        <Key android:codes="98" android:keyLabel="b" android:keyWidth="9%p" />
        <Key android:codes="110" android:keyLabel="n" android:keyWidth="9%p" />
        <Key android:codes="109" android:keyLabel="m" android:keyWidth="9%p" />
        <Key android:codes="-5" android:horizontalGap="1.5%p" android:isRepeatable="true" android:keyWidth="13%p" />
    </Row>
    <Row android:rowEdgeFlags="bottom">
        <Key android:codes="-2" android:keyLabel="123" android:keyWidth="19%p" />
        <Key android:codes="32" android:isRepeatable="false" android:keyLabel="space" android:keyWidth="58%p" />
        <Key android:codes="100860" android:keyEdgeFlags="right" android:keyLabel="#+=" android:keyWidth="19%p" />
    </Row>
</Keyboard>
三、自定义KeyboardView

       如果你对KeyboardView的按键有特定的一些样式的话,那你就要自定义KeyboardView了。
       自定义KeyboardView最重要的就是重写onDraw方法,同时注意一定要写绘制背景再绘制文本。List<Keyboard.Key> keys = getKeyboard().getKeys();这个是获取所有按键信息Keyboard.Key,其中Key.codes是获取xml中设定的code,可以根据这个判定需要绘制的样式不同。

import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Rect;
import android.graphics.Typeface;
import android.graphics.drawable.Drawable;
import android.inputmethodservice.Keyboard;
import android.inputmethodservice.KeyboardView;
import android.support.annotation.DrawableRes;
import android.util.AttributeSet;

import java.lang.reflect.Field;
import java.util.List;

public class CustomKeyboardView extends KeyboardView {

    private Context context;
    private Paint paint;
    private Rect bounds;

    public CustomKeyboardView(Context context, AttributeSet attrs) {
        super(context, attrs);
        paint = new Paint();
        paint.setTextAlign(Paint.Align.CENTER);
        paint.setAntiAlias(true);
        paint.setColor(Color.BLACK);
        bounds = new Rect();
        this.context = context;
    }

    public CustomKeyboardView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        paint = new Paint();
        paint.setTextAlign(Paint.Align.CENTER);
        paint.setAntiAlias(true);
        paint.setColor(Color.BLACK);
        bounds = new Rect();
        this.context = context;
    }

    /**
     * 重写这个方法是为了可以绘制一些特殊按键
     * @param canvas
     */
    @Override
    public void onDraw(Canvas canvas) {
        List<Keyboard.Key> keys = getKeyboard().getKeys();
        for (Keyboard.Key key : keys) {
            if(key.codes[0] != -5 && key.codes[0] != -4){
                //绘制普通信息文本的背景
                drawBackground(R.drawable.keyboard_bg,canvas,key);
                //绘制普通信息的文本信息
                drawText(canvas,key);
            }else{
                //绘制特殊按键
                drawSpecialKey(canvas,key);
            }
        }
    }

    private void drawSpecialKey(Canvas canvas, Keyboard.Key key) {
        if(key.codes[0] == -5){
            drawBackground(R.drawable.keyboard_bg,canvas,key);
            //绘制设置了icon的按钮
            key.icon.setBounds(key.x + (key.width - key.icon.getIntrinsicWidth()) / 2,
                    key.y + (key.height - key.icon.getIntrinsicHeight()) / 2,
                    key.x + (key.width - key.icon.getIntrinsicWidth()) / 2 + key.icon.getIntrinsicWidth(),
                    key.y + (key.height - key.icon.getIntrinsicHeight()) / 2 + key.icon.getIntrinsicHeight());
            key.icon.draw(canvas);
        }else if(key.codes[0] == -4){
            drawBackground(R.drawable.white,canvas,key);
        }
    }

    private void drawBackground(@DrawableRes int drawableId, Canvas canvas, Keyboard.Key key) {
        Drawable drawable = context.getResources().getDrawable(drawableId);
        int[] state = key.getCurrentDrawableState();
        if (key.codes[0] != 0) {
            drawable.setState(state);
        }
        drawable.setBounds(key.x, key.y, key.x + key.width, key.y + key.height);
        drawable.draw(canvas);
    }

    //绘制文本
    private void drawText(Canvas canvas, Keyboard.Key key) {
        if(key.label != null){
            String label = key.label.toString();
            Field field;
            int keyTextSize;
            try {
                //获取KeyboardView设置的默认文本字体大小
                field = KeyboardView.class.getDeclaredField("mLabelTextSize");
                field.setAccessible(true);
                keyTextSize = (int) field.get(this);
                paint.setTextSize(keyTextSize);
                paint.setTypeface(Typeface.DEFAULT);
                paint.getTextBounds(label,0,label.length(),bounds);
                canvas.drawText(label,key.x + (key.width / 2), (key.y + key.height / 2) + bounds.height() / 2, paint);
            } catch (NoSuchFieldException | IllegalAccessException e) {
                e.printStackTrace();
            }
        }
    }
}
四、自定义一个KeyBoard的帮助类

这个类里面主要的作用是使用我们自定义的KeyboardView代替系统自带的KeyboardView进行展示,这里的话主要是需要在EditText获取焦点时候讲弹出软键盘设定成自定义的。

import android.annotation.SuppressLint;
import android.content.Context;
import android.inputmethodservice.Keyboard;
import android.inputmethodservice.KeyboardView;
import android.os.Build;
import android.text.Editable;
import android.view.MotionEvent;
import android.view.View;
import android.view.inputmethod.InputMethodManager;
import android.widget.EditText;

public class KeyboardUtil {

    private Context mContext;
    private KeyboardView mKeyboardView;
    private EditText mEditText;


    @SuppressLint("ClickableViewAccessibility")
    public KeyboardUtil(Context context, KeyboardView keyboardView, EditText editText) {
        this.mContext = context;
        this.mKeyboardView = keyboardView;
        this.mEditText = editText;
        initKeyboard();
        mEditText.setOnTouchListener(new View.OnTouchListener() {
            @Override
            public boolean onTouch(View v, MotionEvent event) {
                if (event.getAction() == MotionEvent.ACTION_UP) {
                    hideSystemKeyboard((EditText) v);
                    mKeyboardView.setVisibility(View.VISIBLE);
                }
                return false;
            }
        });
        mEditText.setOnFocusChangeListener(new View.OnFocusChangeListener() {
            @Override
            public void onFocusChange(View v, boolean hasFocus) {
                if (v instanceof EditText) {
                    if (!hasFocus) {
                        mKeyboardView.setVisibility(View.GONE);
                    } else {
                        hideSystemKeyboard((EditText) v);
                        mKeyboardView.setVisibility(View.VISIBLE);
                    }
                }

            }
        });
    }

    private void hideSystemKeyboard(EditText v) {
        this.mEditText = v;
        InputMethodManager imm = (InputMethodManager) mContext.getSystemService(Context.INPUT_METHOD_SERVICE);
        if(imm == null){
            return;
        }
        boolean isOpen = imm.isActive();
        if (isOpen) {
            imm.hideSoftInputFromWindow(v.getWindowToken(),0);
        }
        if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP){
            v.setShowSoftInputOnFocus(false);
        }else{
            v.setInputType(0);
        }
    }

    private void initKeyboard() {
        Keyboard keyboard = new Keyboard(mContext,R.xml.number_keyboard);
        mKeyboardView.setKeyboard(keyboard);
        mKeyboardView.setEnabled(true);
        mKeyboardView.setOnKeyboardActionListener(listener);
    }

    private KeyboardView.OnKeyboardActionListener listener = new KeyboardView.OnKeyboardActionListener() {
        @Override
        public void onPress(int primaryCode) {
            if(primaryCode == -4 || primaryCode == -5){
                mKeyboardView.setPreviewEnabled(false);
            }else{
                mKeyboardView.setPreviewEnabled(true);
            }
        }

        @Override
        public void onRelease(int primaryCode) {

        }

        @Override
        public void onKey(int primaryCode, int[] keyCodes) {
            Editable editable = mEditText.getText();
            int start = mEditText.getSelectionStart();
            int end = mEditText.getSelectionEnd();
            if(primaryCode == Keyboard.KEYCODE_DONE){
                mKeyboardView.setVisibility(View.GONE);
            }else if(primaryCode == Keyboard.KEYCODE_DELETE){
                if(editable != null && editable.length() > 0){
                    if(start == end){
                        editable.delete(start -1, start);
                    }else{
                        editable.delete(start,end);
                    }
                }
            }else{
                editable.replace(start,end,Character.toString((char) primaryCode));
            }
        }

        @Override
        public void onText(CharSequence text) {

        }

        @Override
        public void swipeLeft() {

        }

        @Override
        public void swipeRight() {

        }

        @Override
        public void swipeDown() {

        }

        @Override
        public void swipeUp() {

        }
    };
}

以上就是自定义软件盘所有相关的操作,这个是比较简单的,如果要负责的话,就需要多研究下,如果有啥没写好的,见谅一下,第一次写。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 160,108评论 4 364
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,699评论 1 296
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 109,812评论 0 244
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 44,236评论 0 213
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,583评论 3 288
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,739评论 1 222
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,957评论 2 315
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,704评论 0 204
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,447评论 1 246
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,643评论 2 249
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,133评论 1 261
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,486评论 3 256
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,151评论 3 238
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,108评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,889评论 0 197
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,782评论 2 277
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,681评论 2 272

推荐阅读更多精彩内容

  • 先上图: 刚开始写自定义软键盘是走了点弯路,用控件去实现,好多小细节处理起来真的太抓狂了… 后来发现 神控件 Ke...
    silence_jjj阅读 4,083评论 0 0
  • 前言 ★本文简述: 简单通过KeyBoardView实现自定义键盘功能。 真的只是给和我一样的渣渣简单介绍,所以了...
    路人葵阅读 28,261评论 11 20
  • 简介 今天在掘金上看了一篇文章,实现自定义软键盘,发现其实实现方式比较简单,不需要改动系统api,只是单纯的加载自...
    fushuang阅读 4,323评论 1 14
  • 发现 关注 消息 iOS 第三方库、插件、知名博客总结 作者大灰狼的小绵羊哥哥关注 2017.06.26 09:4...
    肇东周阅读 11,623评论 4 59
  • 最近项目中在做一个股票交易需求升级, 产品对于输入方式有一些特殊的要求, 具体就是对于输入键盘加了诸多限制. 这就...
    kangqiao182阅读 15,187评论 1 23