Android 自定義軟鍵盤

一、 Android 自定義軟鍵盤開發(fā)流程
  1. 建立軟鍵盤樣式
    ? ? ? ?即在項(xiàng)目res文件夾中創(chuàng)建所需要的各種軟鍵盤的樣式(比如說:字母、數(shù)字蹬耘、符號(hào) 等)欺嗤。
  2. 創(chuàng)建layout布局文件(非必須)
    ? ? ? ?在布局文件中給軟鍵盤創(chuàng)建container,以便顯示軟鍵盤
  3. 自定義KeyboardView
    ? ? ? ?自定義一個(gè)KeyboardView 并繼承自KeyboardView累驮,在自定義的KeyboardView中繪制特殊按鍵酣倾,包括按鍵的點(diǎn)擊背景,圖片谤专,文字 等躁锡。
  4. 自定義一個(gè)普通java類,一般取名為 **Keyboard.java
    ? ? ? ?把軟鍵盤加載到container中毒租,即在布局文件里預(yù)留的存放軟鍵盤 的container稚铣。
    ? ? ? ?在類的內(nèi)部實(shí)現(xiàn)軟鍵盤的輸入控制箱叁,鍵盤轉(zhuǎn)換控制,軟鍵盤的顯示與隱藏控制 等惕医。
    ? ? ? ?在需要用到軟鍵盤的Activity中實(shí)例化該Keyboard 類耕漱,并傳入必要的數(shù)據(jù)和信息。
二抬伺、建立軟鍵盤樣式

? ? ? ?建立軟鍵盤樣式可以直接通過xml進(jìn)行排版螟够,在res/xml中創(chuàng)建一個(gè)根節(jié)點(diǎn)為Keyboard的xml就可以開始排版了
這個(gè)xml中屬性如下:

Keyboard.Key 屬性 介紹
android:codes 此鍵輸出的unicode值或逗號(hào)分隔值。
android:horizontalGap 鍵之間的默認(rèn)水平間隙峡钓。
android:iconPreview 彈出預(yù)覽中顯示的圖標(biāo)妓笙。
android:isModifier 這是否是修改鍵,如Alt或Shift能岩。
android:isRepeatable 是否長按此鍵會(huì)使其重復(fù)寞宫。
android:isSticky 這是否是切換鍵。
android:keyEdgeFlags 關(guān)鍵邊緣標(biāo)志拉鹃。
android:keyHeight 鍵的默認(rèn)高度辈赋,以像素為單位或顯示寬度的百分比。
android:keyIcon 要在鍵上顯示的圖標(biāo)而不是標(biāo)簽膏燕。
android:keyLabel 要在鍵上顯示的標(biāo)簽钥屈。
android:keyOutputText 按下此鍵時(shí)要輸出的字符串。
android:keyWidth 鍵的默認(rèn)寬度坝辫,以像素為單位或顯示寬度的百分比篷就。
android:popupCharacters 要在彈出鍵盤中顯示的字符。
android:popupKeyboard 任何彈出鍵盤的XML鍵盤布局近忙。

實(shí)例代碼:
數(shù)字鍵盤

<?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方法永品,同時(shí)注意一定要寫繪制背景再繪制文本。List<Keyboard.Key> keys = getKeyboard().getKeys();這個(gè)是獲取所有按鍵信息Keyboard.Key击纬,其中Key.codes是獲取xml中設(shè)定的code鼎姐,可以根據(jù)這個(gè)判定需要繪制的樣式不同。

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;
    }

    /**
     * 重寫這個(gè)方法是為了可以繪制一些特殊按鍵
     * @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);
            //繪制設(shè)置了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設(shè)置的默認(rèn)文本字體大小
                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();
            }
        }
    }
}
四更振、自定義一個(gè)KeyBoard的幫助類

這個(gè)類里面主要的作用是使用我們自定義的KeyboardView代替系統(tǒng)自帶的KeyboardView進(jìn)行展示炕桨,這里的話主要是需要在EditText獲取焦點(diǎn)時(shí)候講彈出軟鍵盤設(shè)定成自定義的。

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() {

        }
    };
}

以上就是自定義軟件盤所有相關(guān)的操作肯腕,這個(gè)是比較簡單的献宫,如果要負(fù)責(zé)的話,就需要多研究下实撒,如果有啥沒寫好的姊途,見諒一下涉瘾,第一次寫。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末捷兰,一起剝皮案震驚了整個(gè)濱河市立叛,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌贡茅,老刑警劉巖秘蛇,帶你破解...
    沈念sama閱讀 212,454評論 6 493
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異顶考,居然都是意外死亡赁还,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,553評論 3 385
  • 文/潘曉璐 我一進(jìn)店門驹沿,熙熙樓的掌柜王于貴愁眉苦臉地迎上來艘策,“玉大人,你說我怎么就攤上這事甚负〖砘溃” “怎么了?”我有些...
    開封第一講書人閱讀 157,921評論 0 348
  • 文/不壞的土叔 我叫張陵梭域,是天一觀的道長。 經(jīng)常有香客問我搅轿,道長病涨,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 56,648評論 1 284
  • 正文 為了忘掉前任璧坟,我火速辦了婚禮既穆,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘雀鹃。我一直安慰自己幻工,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,770評論 6 386
  • 文/花漫 我一把揭開白布黎茎。 她就那樣靜靜地躺著囊颅,像睡著了一般。 火紅的嫁衣襯著肌膚如雪傅瞻。 梳的紋絲不亂的頭發(fā)上踢代,一...
    開封第一講書人閱讀 49,950評論 1 291
  • 那天,我揣著相機(jī)與錄音嗅骄,去河邊找鬼胳挎。 笑死,一個(gè)胖子當(dāng)著我的面吹牛溺森,可吹牛的內(nèi)容都是我干的慕爬。 我是一名探鬼主播窑眯,決...
    沈念sama閱讀 39,090評論 3 410
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼医窿!你這毒婦竟也來了磅甩?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 37,817評論 0 268
  • 序言:老撾萬榮一對情侶失蹤留搔,失蹤者是張志新(化名)和其女友劉穎更胖,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體隔显,經(jīng)...
    沈念sama閱讀 44,275評論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡却妨,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,592評論 2 327
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了括眠。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片彪标。...
    茶點(diǎn)故事閱讀 38,724評論 1 341
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖掷豺,靈堂內(nèi)的尸體忽然破棺而出捞烟,到底是詐尸還是另有隱情,我是刑警寧澤当船,帶...
    沈念sama閱讀 34,409評論 4 333
  • 正文 年R本政府宣布题画,位于F島的核電站,受9級特大地震影響德频,放射性物質(zhì)發(fā)生泄漏苍息。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 40,052評論 3 316
  • 文/蒙蒙 一壹置、第九天 我趴在偏房一處隱蔽的房頂上張望竞思。 院中可真熱鬧,春花似錦钞护、人聲如沸盖喷。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,815評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽课梳。三九已至,卻和暖如春步藕,著一層夾襖步出監(jiān)牢的瞬間惦界,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,043評論 1 266
  • 我被黑心中介騙來泰國打工咙冗, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留沾歪,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 46,503評論 2 361
  • 正文 我出身青樓雾消,卻偏偏與公主長得像灾搏,于是被迫代替她去往敵國和親挫望。 傳聞我的和親對象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,627評論 2 350

推薦閱讀更多精彩內(nèi)容

  • 先上圖: 剛開始寫自定義軟鍵盤是走了點(diǎn)彎路狂窑,用控件去實(shí)現(xiàn)媳板,好多小細(xì)節(jié)處理起來真的太抓狂了… 后來發(fā)現(xiàn) 神控件 Ke...
    silence_jjj閱讀 4,361評論 0 0
  • 前言 ★本文簡述: 簡單通過KeyBoardView實(shí)現(xiàn)自定義鍵盤功能。 真的只是給和我一樣的渣渣簡單介紹泉哈,所以了...
    路人葵閱讀 28,758評論 11 20
  • 簡介 今天在掘金上看了一篇文章,實(shí)現(xiàn)自定義軟鍵盤,發(fā)現(xiàn)其實(shí)實(shí)現(xiàn)方式比較簡單,不需要改動(dòng)系統(tǒng)api,只是單純的加載自...
    fushuang閱讀 4,514評論 1 14
  • 發(fā)現(xiàn) 關(guān)注 消息 iOS 第三方庫蛉幸、插件、知名博客總結(jié) 作者大灰狼的小綿羊哥哥關(guān)注 2017.06.26 09:4...
    肇東周閱讀 12,066評論 4 62
  • 最近項(xiàng)目中在做一個(gè)股票交易需求升級, 產(chǎn)品對于輸入方式有一些特殊的要求, 具體就是對于輸入鍵盤加了諸多限制. 這就...
    kangqiao182閱讀 15,252評論 1 23