天天看點

自定義數字鍵盤類似于系統鍵盤

最近因為項目需求要自定義一個數字鍵盤,在網上查了些資料但很多功能不全,很多場景不能适應。今天就結合網上的一些資料打算自己寫一個鍵盤。主要适應二種場景:

1、支付密碼 需要随機鍵盤

2、點選EditText能自動向上頂布局

源碼下載下傳位址:http://git.oschina.net/shikh/CustomView-master

先看一下使用方法:

<expand.shikh.com.customview.keyboard.NumberKeyBoardEditText
        android:layout_alignParentBottom="true"
        android:id="@+id/etChar"
        android:layout_width="match_parent"
        android:layout_height="50dp"
        android:layout_marginTop="10dp"
        android:background="#ace"
        android:gravity="center"
        android:hint="數字鍵盤"
        android:singleLine="true"
        android:textColor="#ffffff"
        android:textColorHint="#ffffff"
        mykeyboard:xml="@xml/symbols"
        mykeyboard:randomkeys="true"
        />
           

隻需引用我們自定義的editText 控件就歐了~

先看一下效果圖

自定義數字鍵盤類似于系統鍵盤

下面來說說我們的實作方式:

實作軟鍵盤主要用到了系統的兩個類Keyboard和KeyboardView:

Keyboard類源碼的介紹是: Listener for virtual keyboard events.即用于監聽虛拟鍵盤。至于Keyboard類的映射機制,這裡就不需要說了,要分析源碼請移駕http://blog.csdn.net/pi9nc/article/details/27304459

1、在res檔案下,建立一個xml檔案,下面放數字鍵盤的布局檔案

<?xml version="1.0" encoding="utf-8"?>
<Keyboard xmlns:android="http://schemas.android.com/apk/res/android"
    android:horizontalGap="1dp"
    android:keyWidth="33.33333%p"
    android:verticalGap="1dp">
    <Row android:keyHeight="7%p">
        <Key android:codes="-3" android:keyIcon="@mipmap/keyboard_back_img" android:keyWidth="100%p" />
    </Row>
    <Row  android:keyHeight="10%p">
        <Key android:codes="55" android:keyLabel="7" />
        <Key android:codes="56" android:keyLabel="8" />
        <Key android:codes="57" android:keyEdgeFlags="right" android:keyLabel="9" />
    </Row>
    <Row  android:keyHeight="10%p">
        <Key android:codes="52" android:keyLabel="4" />
        <Key android:codes="53" android:keyLabel="5" />
        <Key android:codes="54" android:keyEdgeFlags="right" android:keyLabel="6" />

    </Row>
    <Row  android:keyHeight="10%p">
        <Key android:codes="49" android:keyLabel="1" />
        <Key android:codes="50" android:keyLabel="2" />
        <Key android:codes="51" android:keyEdgeFlags="right" android:keyLabel="3" />
    </Row>
    <Row  android:keyHeight="10%p">
        <Key android:codes="4896" android:keyLabel="清空" />
        <!--<Key
            android:codes="46"
            android:keyLabel="." />-->
        <Key android:codes="48" android:keyLabel="0" />
        <!--<Key android:codes="-3" android:keyEdgeFlags="right" android:keyLabel="OK"/>-->
        <!--<key android:codes="4896" android:keylabel="清空" />-->
        <Key android:codes="-5" android:keyEdgeFlags="right" android:keyIcon="@mipmap/keyboard_delete_img" />
    </Row>
</Keyboard>
           

3、建立鍵盤的工具類KeyboardUtil(顯示鍵盤的主要代碼),如下:

public class KeyboardUtil {
    private KeyboardView keyboardView;
    private Keyboard k;// 數字鍵盤
    private EditText ed;
    private Context mContext;
    private PopupWindow mKeyboardWindow;

    public Keyboard getK() {
        return k;
    }

    public KeyboardView getKeyboardView() {
        return keyboardView;
    }

    public void setmKeyboardWindow(PopupWindow mKeyboardWindow) {
        this.mKeyboardWindow = mKeyboardWindow;
    }

    /**
     * 鍵盤置于布局檔案中
     * @author shikh
     * @time 2016/12/8 上午10:19
     */
    public KeyboardUtil(Activity atx, EditText edit,int keyId,int xmlId){
        this.ed = edit;
        this.mContext = atx;
        k = new Keyboard(atx, xmlId == ?R.xml.symbols:xmlId);
        keyboardView = (KeyboardView) atx.findViewById(keyId);
        initKeyBoard();
    }

    /**
     * 鍵盤用popwindow 形式打開 結合 numberKeyBoardEditText 使用 具有向上頂布局的功能
     * @author shikh
     * @time 2016/12/8 上午10:22
     */
    public KeyboardUtil(Context ctx, EditText edit, int xmlId) {
        this.ed = edit;
        this.mContext = ctx;
        k = new Keyboard(ctx, xmlId == ?R.xml.symbols:xmlId);
        keyboardView = (KeyboardView) View.inflate(ctx,R.layout.layout_keyboard_view,null);
    }


    public void initKeyBoard(){
        keyboardView.setKeyboard(k);
        keyboardView.setEnabled(true);
        keyboardView.setPreviewEnabled(false);
        keyboardView.setVisibility(View.VISIBLE);
        keyboardView.setOnKeyboardActionListener(listener);
    }
    private KeyboardView.OnKeyboardActionListener listener = new KeyboardView.OnKeyboardActionListener() {
        @Override
        public void swipeUp() {}

        @Override
        public void swipeRight() {}

        @Override
        public void swipeLeft() {}

        @Override
        public void swipeDown() {}

        @Override
        public void onText(CharSequence text) {}

        @Override
        public void onRelease(int primaryCode) {}

        @Override
        public void onPress(int primaryCode) {}

        //一些特殊操作按鍵的codes是固定的比如完成、回退等
        @Override
        public void onKey(int primaryCode, int[] keyCodes) {
            Editable editable = ed.getText();
            int start = ed.getSelectionStart();
            if (primaryCode == Keyboard.KEYCODE_DELETE) {// 回退
                if (editable != null && editable.length() > ) {
                    if (start > ) {
                        editable.delete(start - , start);
                    }
                }
            } else if (primaryCode == ) {// 清空
                editable.clear();
            } else if (primaryCode == Keyboard.KEYCODE_CANCEL) {//
                hideKeyboard();
            } else if (primaryCode == ) { // 小數點
                String text = ed.getText().toString();
                if (!text.contains(".") && text.length() >) {
                    editable.insert(start, Character.toString((char) primaryCode));
                }
            } else { //将要輸入的數字現在編輯框中
                editable.insert(start, Character.toString((char) primaryCode));
            }
        }
    };



    /**
     * 實應于 鍵盤布局放置在activity 布局檔案中
     * @author shikh
     * @time 2016/12/8 上午10:08
     */
    public boolean isShow() {
        int visibility = keyboardView.getVisibility();
        if (visibility == View.VISIBLE) {
            return true;
        }
        return false;
    }
    public void showKeyboard() {
        int visibility = keyboardView.getVisibility();
        if (visibility == View.GONE || visibility == View.INVISIBLE) {
            keyboardView.setVisibility(View.VISIBLE);
        }
    }

    public void hideKeyboard() {
        if (null != mKeyboardWindow) {
            if (mKeyboardWindow.isShowing()) {
                mKeyboardWindow.dismiss();
            }
        }else {
            int visibility = keyboardView.getVisibility();
            if (visibility == View.VISIBLE) {
                keyboardView.setVisibility(View.GONE);
            }
        }

    }
    /**
     * 隐藏系統鍵盤
     * @author shikh
     * @time 2016/12/8 下午3:19
     */
    public void hideSoftInputMethod(Window mWindow) {
        mWindow.setSoftInputMode(
                WindowManager.LayoutParams.SOFT_INPUT_STATE_ALWAYS_HIDDEN);
        int currentVersion = android.os.Build.VERSION.SDK_INT;
        String methodName = null;
        if (currentVersion >= ) {
            // 4.2
            methodName = "setShowSoftInputOnFocus";
        } else if (currentVersion >= ) {
            // 4.0
            methodName = "setSoftInputShownOnFocus";
        }
        if (methodName == null) {
            ed.setInputType(InputType.TYPE_NULL);
        } else {
            Class<EditText> cls = EditText.class;
            Method setShowSoftInputOnFocus;
            try {
                setShowSoftInputOnFocus = cls.getMethod(methodName,
                        boolean.class);
                setShowSoftInputOnFocus.setAccessible(true);
                setShowSoftInputOnFocus.invoke(ed, false);
            } catch (NoSuchMethodException e) {
                ed.setInputType(InputType.TYPE_NULL);
                e.printStackTrace();
            } catch (IllegalAccessException e) {
                e.printStackTrace();
            } catch (IllegalArgumentException e) {
                e.printStackTrace();
            } catch (InvocationTargetException e) {
                e.printStackTrace();
            }
        }
    }
}
           

4、自定義EditText 控件NumberKeyBoardEditText ,實作觸摸彈出自定義鍵盤,先要建立一個 KeyBoardView 布局供 popupWindow使用

<?xml version="1.0" encoding="utf-8"?>
<android.inputmethodservice.KeyboardView xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/keyboard_view"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:layout_alignParentBottom="true"
    android:background="#dddddd"
    android:focusable="true"
    android:focusableInTouchMode="true"
    android:keyBackground="@drawable/bg_keyboard_btn"
    android:keyTextColor="@color/keyTextColor"
    android:keyTextSize="24sp"
    android:paddingTop="1dp"
    android:shadowRadius="0.0"
    android:visibility="gone" />
           

NumberKeyBoardEditText 類的主要代碼

public NumberKeyBoardEditText(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        initAttributes(context);
        initKeyBoard(context, attrs);
        initPopWindow();
    }

    private void initKeyBoard(Context context, AttributeSet attrs) {
        TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.keyboard);
        if (a.hasValue(R.styleable.keyboard_xml)) {
            needcustomkeyboard = true;
            int xmlid = a.getResourceId(R.styleable.keyboard_xml, );
            keyboardUtil = new KeyboardUtil(context, this, xmlid);
        } else {
            keyboardUtil = new KeyboardUtil(context, this, );
        }
        if (a.hasValue(R.styleable.keyboard_randomkeys)) {
            randomkeys = a.getBoolean(R.styleable.keyboard_randomkeys, false);
        }

        mKeyboardView = keyboardUtil.getKeyboardView();
        mKeyboard = keyboardUtil.getK();
        if (randomkeys) {//随機鍵盤
            randomdigkey(mKeyboard);
        }
        keyboardUtil.initKeyBoard();
        a.recycle();
    }

    private void initPopWindow() {
        mKeyboardWindow = new PopupWindow(keyboardUtil.getKeyboardView(), ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT);
        mKeyboardWindow.setAnimationStyle(R.style.AnimationFade);//設定動畫效果,檔案在資源裡面,這裡就不貼出來。</span></strong>
        // mKeyboardWindow.setBackgroundDrawable(new BitmapDrawable());
        // mKeyboardWindow.setOutsideTouchable(true);
        keyboardUtil.setmKeyboardWindow(mKeyboardWindow);
        mKeyboardWindow.setOnDismissListener(new PopupWindow.OnDismissListener() {

            @Override
            public void onDismiss() {
                // TODO Auto-generated method stub
                if (scrolldis > ) {
                    int temp = scrolldis;
                    scrolldis = ;
                    if (null != mContentView) {
                        //使布局整體向上頂的關鍵代碼,使用布局的scrollBy重新滾動位置
                        mContentView.scrollBy(, -temp);
                    }
                }
            }
        });

    }
           
// 暫時未使用到,為了實作随機鍵盤布局
    private void randomdigkey(Keyboard mKeyboard) {
        if (mKeyboard == null) {
            return;
        }
        List<Keyboard.Key> keyList = mKeyboard.getKeys();
        // 查找出0-9的數字鍵
        List<Keyboard.Key> newkeyList = new ArrayList<Keyboard.Key>();
        for (int i = , size = keyList.size(); i < size; i++) {
            Keyboard.Key key = keyList.get(i);
            CharSequence label = key.label;
            if (label != null && isNumber(label.toString())) {
                newkeyList.add(key);
            }
        }

        int count = newkeyList.size();
        List<KeyModel> resultList = new ArrayList<KeyModel>();
        LinkedList<KeyModel> temp = new LinkedList<KeyModel>();

        for (int i = ; i < count; i++) {
            temp.add(new KeyModel( + i, i + ""));
        }
        Random rand = new SecureRandom();
        rand.setSeed(SystemClock.currentThreadTimeMillis());

        for (int i = ; i < count; i++) {
            int num = rand.nextInt(count - i);
            KeyModel model = temp.get(num);
            resultList.add(new KeyModel(model.getCode(), model.getLable()));
            temp.remove(num);
        }

        for (int i = , size = newkeyList.size(); i < size; i++) {
            Keyboard.Key newKey = newkeyList.get(i);
            KeyModel resultmodle = resultList.get(i);
            newKey.label = resultmodle.getLable();
            newKey.codes[] = resultmodle.getCode();
        }

    }
           
@Override
    public boolean onTouchEvent(MotionEvent event) {
        super.onTouchEvent(event);
        requestFocus();
        requestFocusFromTouch();
//        if (needcustomkeyboard) {
            hideSysInput();
            showKeyboard();
//        }
        return true;
    }

    @Override
    public boolean onKeyDown(int keyCode, KeyEvent event) {
        if (keyCode == KeyEvent.KEYCODE_BACK) {
            if (null != mKeyboardWindow) {
                if (mKeyboardWindow.isShowing()) {
                    mKeyboardWindow.dismiss();
                    return true;
                }
            }
        }
        return super.onKeyDown(keyCode, event);
    }


    @Override
    protected void onAttachedToWindow() {
        super.onAttachedToWindow();
        this.mWindow = ((Activity) getContext()).getWindow();
        this.mDecorView = this.mWindow.getDecorView();
        this.mContentView = this.mWindow.findViewById(Window.ID_ANDROID_CONTENT);
//        hideSysInput();
    }

    @Override
    public void onDetachedFromWindow() {
        super.onDetachedFromWindow();
        hideKeyboard();
        mKeyboardWindow = null;
        mKeyboardView = null;
        mKeyboard = null;
        mDecorView = null;
        mContentView = null;
        mWindow = null;
    }