自定义软键盘(Android)

去年因公司需要给电视盒子做一个自定义键盘,今天偶然想起来, 想趁此机会记录一下。
当时好像是下载了一个别人的例子,参考着改了一下。具体是哪个作者分享的demo已无法追寻了,先在此表示感谢。
此demo功能中,只能输入英文和常用字符,尚未兼顾输入中文功能。
效果图如下:



1.继承KeyboardView,重写onKeyDown方法:

public class CKeyboardView extends KeyboardView {

    private Keyboard currentKeyboard;
    private List<Key> keys = new ArrayList<Key>();
    private int lastKeyIndex = 0;
    private Key focusedKey;//当前获取焦点的key
    private Rect rect;//表示包裹当前获取焦点的key的方框

    private int nCurKeyboardKeyNums;//当前键盘的按键数量
    private Keyboard nCurrentKeyboard;
    private List<Key> nKeys;//按键集合
    private int nLastKeyIndex = 0;

    public CKeyboardView(Context context, AttributeSet attrs) {
        super(context, attrs);

    }

    @Override
    public void onDraw(Canvas canvas) {
        // TODO Auto-generated method stub
        super.onDraw(canvas);
        //获取当前键盘
        currentKeyboard = this.getKeyboard();
        keys = currentKeyboard.getKeys();
        //当前获取焦点的key画方框,表示选中的是当前key
        Paint p = new Paint();
        p.setColor(Color.parseColor("#33ffff"));
        p.setStyle(Style.STROKE);
        p.setStrokeWidth(3.75f);
        focusedKey = keys.get(lastKeyIndex);
        rect = new Rect(focusedKey.x, focusedKey.y + 4, focusedKey.x
                + focusedKey.width, focusedKey.y + focusedKey.height);
        canvas.drawRect(rect, p);
    }

    public int getLastKeyIndex() {
        return lastKeyIndex;
    }

    public void setLastKeyIndex(int lastKeyIndex) {
        this.lastKeyIndex = lastKeyIndex;
    }

    private void setFields() {
        nCurrentKeyboard = this.getKeyboard();
        nKeys = nCurrentKeyboard.getKeys();
        nCurKeyboardKeyNums = nKeys.size();
        nLastKeyIndex = this.getLastKeyIndex();
    }

    @Override
    public boolean onKeyDown(int keyCode, KeyEvent event) {
        //Toast.makeText(this.getContext(), "onKeyDown--"+keyCode, Toast.LENGTH_LONG).show();
        Log.d("tag", keyCode + "---" + event.getAction());
        switch (keyCode) {
            case KeyEvent.KEYCODE_BACK:// 返回 4
                if (event.getRepeatCount() == 0) {
                    if (this.handleBack()) {
                        return true;
                    }
                }
                break;
            case KeyEvent.KEYCODE_DPAD_DOWN:// 向下 20
                setFields();

                if (nLastKeyIndex >= nCurKeyboardKeyNums - 1) {
                    this.setLastKeyIndex(0);
                } else {
                    int x = nKeys.get(nLastKeyIndex).x;
                    int y = nKeys.get(nLastKeyIndex).y;
                    int[] nearestKeys = nCurrentKeyboard.getNearestKeys(x, y);

                    for (int index : nearestKeys) {
                        if (nLastKeyIndex < index) {
                            Key nearKey = nKeys.get(index);
                            Key lastKey = nKeys.get(nLastKeyIndex);
                            if (((lastKey.x >= nearKey.x) // left side compare
                                    && (lastKey.x < (nearKey.x + nearKey.width)))
                                    || (((lastKey.x + lastKey.width) > nearKey.x) // right
                                    // side
                                    // compare
                                    && ((lastKey.x + lastKey.width) <= (nearKey.x + nearKey.width)))) {
                                this.setLastKeyIndex(index);
                                break;
                            }
                        }
                    }

                }
                this.invalidate();
                return true;
            case KeyEvent.KEYCODE_DPAD_UP:// 向上19
                setFields();

                if (nLastKeyIndex <= 0) {
                    this.setLastKeyIndex(nCurKeyboardKeyNums - 1);
                } else {
                    int x = nKeys.get(nLastKeyIndex).x;
                    int y = nKeys.get(nLastKeyIndex).y;
                    int[] nearestKeys = nCurrentKeyboard.getNearestKeys(x, y);

                    for (int i = nearestKeys.length - 1; i >= 0; i--) {
                        int index = nearestKeys[i];
                        if (nLastKeyIndex > index) {
                            Key nearKey = nKeys.get(index);
                            Key nextNearKey = nKeys.get(index + 1);
                            Key lastKey = nKeys.get(nLastKeyIndex);
                            if ((lastKey.x >= nearKey.x && lastKey.x < (nearKey.x + nearKey.width))
                                    && ((lastKey.x + lastKey.width) <= (nextNearKey.x + nextNearKey.width) || lastKey.x
                                    + lastKey.width > nextNearKey.x)) {
                                this.setLastKeyIndex(index);
                                break;
                            }
                        }
                    }
                }
                this.invalidate();
                return true;
            case KeyEvent.KEYCODE_DPAD_LEFT:// 左21
                setFields();

                if (nLastKeyIndex <= 0) {
                    this.setLastKeyIndex(nCurKeyboardKeyNums - 1);
                } else {
                    nLastKeyIndex--;
                    this.setLastKeyIndex(nLastKeyIndex);
                }
                this.invalidate();
                return true;
            case KeyEvent.KEYCODE_DPAD_RIGHT:// 右22

                setFields();
                if (nLastKeyIndex >= nCurKeyboardKeyNums - 1) {
                    this.setLastKeyIndex(0);
                } else {
                    nLastKeyIndex++;
                    this.setLastKeyIndex(nLastKeyIndex);
                }
                this.invalidate();
                return true;
            case KeyEvent.KEYCODE_ENTER://66
            case KeyEvent.KEYCODE_DPAD_CENTER://23
                setFields();
                int currentKeyCode = nKeys.get(nLastKeyIndex).codes[0];

                switch (currentKeyCode) {
                    case Keyboard.KEYCODE_MODE_CHANGE:
                    case Keyboard.KEYCODE_CANCEL:
                    case Keyboard.KEYCODE_DELETE:
                    case Keyboard.KEYCODE_SHIFT:
                        getOnKeyboardActionListener().onKey(currentKeyCode, null);
                        return true;

                    default:
                        getOnKeyboardActionListener().onKey(currentKeyCode, null);
                        return true;
                }
        }
        return super.onKeyDown(keyCode, event);
    }
}

2.创建2个xml,表示字母键盘和符号数字键盘。
qwenty.xml

<?xml version="1.0" encoding="UTF-8"?>
<Keyboard xmlns:android="http://schemas.android.com/apk/res/android"
    android:horizontalGap="0.0px"
    android:keyHeight="8%"
    android:keyWidth="10.000002%p"
    android:verticalGap="0.0px">
           
    <Row>
                       
        <Key
            android:codes="113"
            android:keyEdgeFlags="left"
            android:keyLabel="q" />

        <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:keyEdgeFlags="right"
            android:keyLabel="p" />
    </Row>

    <Row>
        <Key
            android:codes="97"
            android:horizontalGap="4.999995%p"
            android:keyEdgeFlags="left"
            android:keyLabel="a" />

        <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:keyEdgeFlags="right"
            android:keyLabel="l" />
    </Row>

    <Row>
        <Key
            android:codes="-1"
            android:isModifier="true"
            android:isSticky="true"
            android:keyEdgeFlags="left"
            android:keyIcon="@drawable/sym_keyboard_shift"
            android:keyWidth="14.999998%p" />

        <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="-5"
            android:isRepeatable="true"
            android:keyEdgeFlags="right"
            android:keyIcon="@drawable/sym_keyboard_delete"
            android:keyWidth="14.999998%p" />
               
    </Row>
           
    <Row android:rowEdgeFlags="bottom">
                       
        <Key
            android:codes="-2"
            android:keyLabel="12#"
            android:keyWidth="20.000004%p" />

        <Key
            android:codes="44"
            android:keyLabel=","
            android:keyWidth="14.999998%p" />

        <Key
            android:codes="32"
            android:isRepeatable="true"
            android:keyIcon="@drawable/sym_keyboard_space"
            android:keyWidth="29.999996%p" />
                       
        <Key
            android:codes="46"
            android:keyLabel="."
            android:keyWidth="14.999998%p" />
                       
        <Key
            android:codes="-3"
            android:keyEdgeFlags="right"
            android:keyLabel="完成"
            android:keyWidth="20.000004%p" />
    </Row>

</Keyboard>

symbols.xml

<?xml version="1.0" encoding="utf-8"?>
<Keyboard xmlns:android="http://schemas.android.com/apk/res/android"
    android:horizontalGap="0px"
    android:keyHeight="8%"
    android:keyWidth="10%p"
    android:verticalGap="0px" >

    <Row>
        <Key
            android:codes="49"
            android:keyLabel="1" />
        <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" />
    </Row>

    <Row>
        <Key
            android:codes="64"
            android:keyLabel="@string/xml_at" />
        <Key
            android:codes="35"
            android:keyLabel="#" />
        <Key
            android:codes="36"
            android:keyLabel="$" />
        <Key
            android:codes="37"
            android:keyLabel="%" />
        <Key
            android:codes="38"
            android:keyLabel="&" />
        <Key
            android:codes="42"
            android:keyLabel="*" />
        <Key
            android:codes="45"
            android:keyLabel="-" />
        <Key
            android:codes="61"
            android:keyLabel="=" />
        <Key
            android:codes="40"
            android:keyLabel="(" />
        <Key
            android:codes="41"
            android:keyLabel=")" />
    </Row>
    <Row>
        <Key
            android:codes="95"
            android:keyLabel="_" />
        <Key
            android:codes="33"
            android:keyLabel="!" />
        <Key
            android:codes="34"
            android:keyLabel=""" />
        <Key
            android:codes="39"
            android:keyLabel="'" />
        <Key
            android:codes="58"
            android:keyLabel=":" />
        <Key
            android:codes="59"
            android:keyLabel=";" />
        <Key
            android:codes="47"
            android:keyLabel="/" />
        <Key
            android:codes="63"
            android:keyLabel="@string/xml_wh" />
        <Key
            android:codes="43"
            android:keyLabel="+" />
        <Key
            android:codes="-5"
            android:isRepeatable="true"
            android:keyEdgeFlags="right"
            android:keyIcon="@drawable/sym_keyboard_delete"
             />
    </Row>
    <Row android:rowEdgeFlags="bottom" >
        <Key
            android:codes="-2"
            android:keyLabel="ABC"
            android:keyWidth="20%p" />
                
        <Key
            android:codes="60"
            android:keyLabel="<"
            android:keyWidth="12%p"/>

        <Key
            android:codes="62"
            android:keyLabel=">"
            android:keyWidth="12%p" />
        <Key
            android:codes="32"
            android:isRepeatable="true"
            android:keyIcon="@drawable/sym_keyboard_space"
           android:keyWidth="12%p"  />

        <Key
            android:codes="44"
            android:keyLabel=","
           android:keyWidth="12%p"/>
        <Key
            android:codes="46"
            android:keyLabel="."
            android:keyWidth="12%p" />

        <Key
            android:codes="-3"
            android:keyEdgeFlags="right"
            android:keyLabel="完成"
            android:keyWidth="20%p" />
    </Row>

</Keyboard>

3.创建KeyboardUtil,控制键盘

public class KeyboardUtil {

    private Context context;
    private KeyboardView keyboardView;
    private Keyboard kQwerty;//字母键盘
    private Keyboard kSymbols;//符号键盘
    private boolean isNum = false;//是否是数字
    private boolean isSupper = false;//是否大写
    private EditText editText;
    private boolean isShown = false;

    /**
     * 构造方法
     *
     * @param context  上下文
     * @param editText 需要输入的EditView
     * @param pView    KeyboardView所在的父控件
     */
    public KeyboardUtil(Context context, EditText editText, View pView) {
        this.context = context;
        this.editText = editText;
        //创建字母和符号键盘
        kQwerty = new Keyboard(context, R.xml.qwerty);
        kSymbols = new Keyboard(context, R.xml.symbols);
        //找到KeyboardView
        this.keyboardView = (KeyboardView) pView
                .findViewById(R.id.keyboard_view);
        this.keyboardView.setKeyboard(kQwerty);
        this.keyboardView.setEnabled(true);
        this.keyboardView.setPreviewEnabled(false);
        this.keyboardView.setOnKeyboardActionListener(listener);
    }

    //Keyboard监听
    OnKeyboardActionListener listener = new OnKeyboardActionListener() {

        @Override
        public void swipeUp() {
            // TODO Auto-generated method stub

        }

        @Override
        public void swipeRight() {
            // TODO Auto-generated method stub

        }

        @Override
        public void swipeLeft() {
            // TODO Auto-generated method stub

        }

        @Override
        public void swipeDown() {
            // TODO Auto-generated method stub

        }

        @Override
        public void onText(CharSequence text) {
            // TODO Auto-generated method stub

        }

        @Override
        public void onRelease(int primaryCode) {
            // TODO Auto-generated method stub

        }

        @Override
        public void onPress(int primaryCode) {
            // TODO Auto-generated method stub

        }

        @Override
        public void onKey(int primaryCode, int[] keyCodes) {
            // Toast.makeText(context, "onKey--"+primaryCode,
            // Toast.LENGTH_LONG).show();

            Editable editable = editText.getText();
            int start = editText.getSelectionStart();

            if (primaryCode == Keyboard.KEYCODE_CANCEL) {
                // 取消,隐藏键盘
                hideKeyboard();
            } else if (primaryCode == Keyboard.KEYCODE_DELETE) {
                // 删除回退
                if (editable != null && editable.length() > 0) {
                    if (start > 0) {
                        editable.delete(start - 1, start);
                    }
                }
            } else if (primaryCode == Keyboard.KEYCODE_SHIFT) {
                // 大小写切换
                changeKey();
                keyboardView.setKeyboard(kQwerty);
            } else if (primaryCode == Keyboard.KEYCODE_MODE_CHANGE) {
                ((CKeyboardView) keyboardView).setLastKeyIndex(0);
                // 数字键盘切换
                if (isNum) {
                    keyboardView.setKeyboard(kQwerty);
                } else {
                    keyboardView.setKeyboard(kSymbols);
                }
                isNum = !isNum;
            } else {
                editable.insert(start, Character.toString((char) primaryCode));
            }

        }
    };

    public KeyboardView getKeyboardView() {
        return keyboardView;
    }

    public EditText getEditText() {
        return editText;
    }

    public void setEditText(EditText editText) {
        this.editText = editText;
    }

    //变换大小写
    private void changeKey() {
        List<Key> keys = kQwerty.getKeys();

        for (Key key : keys) {
            if (key.label != null && isWord(String.valueOf(key.label))) {
                if (isSupper) {// up to low
                    key.label = key.label.toString().toLowerCase();
                    key.codes[0] = key.codes[0] + 32;
                } else {
                    key.label = key.label.toString().toUpperCase();
                    key.codes[0] = key.codes[0] - 32;
                }
            }
        }

        isSupper = !isSupper;

    }

    private boolean isWord(String s) {
        String reg = "[a-zA-Z]{1}";
        return s.matches(reg);
    }
    //显示键盘
    public void showKeyboard() {
        int visibility = keyboardView.getVisibility();
        if (visibility == View.GONE || visibility == View.INVISIBLE) {
            keyboardView.setVisibility(View.VISIBLE);
            setShown(true);
        }
    }
    //隐藏键盘
    public void hideKeyboard() {
        if (keyboardView.getVisibility() == View.VISIBLE) {
            keyboardView.setVisibility(View.GONE);
            setShown(false);
        }
    }

    public boolean isShown() {
        return isShown;
    }

    public void setShown(boolean isShown) {
        this.isShown = isShown;
    }

}

4.在布局中使用KeyboardView,在Activity中调用

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    tools:context="com.cykj.keyboarddemo.MainActivity">

    <EditText
        android:id="@+id/edit"
        android:layout_width="368dp"
        android:layout_height="wrap_content" />

    <RelativeLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content">

        <com.cykj.keyboarddemo.CKeyboardView
            android:id="@+id/keyboard_view"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_alignParentBottom="true"
            android:focusableInTouchMode="true"
            android:visibility="gone" />
        <!--android:keyTextColor="@android:color/white"
              android:background="#ff202020"
            android:keyBackground="@drawable/btn_keyboard_key"
        -->
    </RelativeLayout>
</LinearLayout>

public class MainActivity extends AppCompatActivity implements View.OnClickListener {

    private KeyboardUtil keyboardUtil;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        View view = LayoutInflater.from(this).inflate(R.layout.activity_main, null);
        setContentView(view);
        EditText editText = (EditText) findViewById(R.id.edit);
        editText.setOnClickListener(this);

        keyboardUtil = new KeyboardUtil(this, editText, view);
    }

    @Override
    public void onClick(View v) {
        switch (v.getId()) {
            case R.id.edit:
                if (keyboardUtil.isShown()
                        && keyboardUtil.getEditText().getId() == v.getId()) {
                    keyboardUtil.hideKeyboard();
                } else {
                    keyboardUtil.setEditText((EditText) v);
                    keyboardUtil.showKeyboard();
                }
                break;
        }
    }

    @Override
    public void onBackPressed() {
        // 如果输入法显示,则隐藏输入法,不关闭PopupWindow
        if (keyboardUtil != null && keyboardUtil.isShown()) {
            keyboardUtil.hideKeyboard();
        } else {
            super.onBackPressed();
        }
    }
}

demo地址

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

推荐阅读更多精彩内容