EditText的輸入類型為NumbberPassword時,小米數(shù)字軟鍵盤無法監(jiān)聽到鍵盤的刪除按鈕事件

今天測試提了一個bug,在小米手機(jī)上,就是一個自定義的密碼框,輸錯密碼,想要刪除,按軟鍵盤的刪除鍵,卻發(fā)現(xiàn)沒有反應(yīng),但是在其他的手機(jī)上卻可以刪除。這一聽就頭大,很明顯的系統(tǒng)不同造成的。但是bug提了,能怎么辦,使出獨(dú)門絕學(xué)百度大法。

一般正常的我們監(jiān)聽軟鍵盤的回調(diào)就好了。如下:

editText.setSoftKeyListener(new OnKeyListener() {
            @Override
            public boolean onKey(View v, int keyCode, KeyEvent keyEvent) {
                if ((keyCode == KeyEvent.KEYCODE_DEL || keyCode == KeyEvent.KEYCODE_FORWARD_DEL ) && keyEvent.getAction() == KeyEvent.ACTION_DOWN && mCodes.size() > 0) {
                    mCodes.remove(mCodes.size() - 1);
                    showCode();
                    return true;
                }
                return false;
            }
        });

但是在小米手機(jī)上這種方式不起效。經(jīng)過百度查的,有些文章也說在谷歌輸入法上也不其效果。

/**
     * Register a callback to be invoked when a hardware key is pressed in this view.
     * Key presses in software input methods will generally not trigger the methods of
     * this listener.
     * @param l the key listener to attach to this view
     */
    public void setOnKeyListener(OnKeyListener l) {
        getListenerInfo().mOnKeyListener = l;
    }


/**
     * Interface definition for a callback to be invoked when a hardware key event is
     * dispatched to this view. The callback will be invoked before the key event is
     * given to the view. This is only useful for hardware keyboards; a software input
     * method has no obligation to trigger this listener.
     */
    public interface OnKeyListener {
        /**
         * Called when a hardware key is dispatched to a view. This allows listeners to
         * get a chance to respond before the target view.
         * <p>Key presses in software keyboards will generally NOT trigger this method,
         * although some may elect to do so in some situations. Do not assume a
         * software input method has to be key-based; even if it is, it may use key presses
         * in a different way than you expect, so there is no way to reliably catch soft
         * input key presses.
         *
         * @param v The view the key has been dispatched to.
         * @param keyCode The code for the physical key that was pressed
         * @param event The KeyEvent object containing full information about
         *        the event.
         * @return True if the listener has consumed the event, false otherwise.
         */
        boolean onKey(View v, int keyCode, KeyEvent event);
    }

注釋的大概意思是這個監(jiān)聽器是用于監(jiān)聽實體鍵的key event的,雖然輸入法也可以發(fā)出key event,但是這種事是看緣分的。比如搜狗輸入法就是基于keyEvent和EditText交互的,但谷歌輸入法就不會發(fā)出keyEvent來告知EditText有輸入事件,所以用這個監(jiān)聽器來監(jiān)聽軟鍵盤的輸入和點擊事件是不靠譜的。

谷歌輸入法是通過InputConnection類,InputConnection 是輸入法和應(yīng)用內(nèi)View(通常是EditText)交互的通道,輸入法的文本輸入和刪改事件,包括key event事件都是通過InputConnection發(fā)送給EditText。

InputConnection有幾個關(guān)鍵方法,通過重寫這幾個方法,我們基本可以攔截軟鍵盤的所有輸入和點擊事件:

//當(dāng)輸入法輸入了字符,包括表情,字母、文字、數(shù)字和符號等內(nèi)容,會回調(diào)該方法
public boolean commitText(CharSequence text, int newCursorPosition) 

//當(dāng)有按鍵輸入時,該方法會被回調(diào)。比如點擊退格鍵時,搜狗輸入法應(yīng)該就是通過調(diào)用該方法,
//發(fā)送keyEvent的,但谷歌輸入法卻不會調(diào)用該方法,而是調(diào)用下面的deleteSurroundingText()方法。  
public boolean sendKeyEvent(KeyEvent event);   

//當(dāng)有文本刪除操作時(剪切,點擊退格鍵),會觸發(fā)該方法 
public boolean deleteSurroundingText(int beforeLength, int afterLength) 

//結(jié)束組合文本輸入的時候,回調(diào)該方法
public boolean finishComposingText();

當(dāng)輸入法要和指定View建立連接的時候,系統(tǒng)會通過該方法返回一個InputConnection 實例給輸入法。所以我們要復(fù)寫EditText的這個方法,返回我們自己的InputConnection 。其原理圖如下:

12a4dc4ff2647affa8bea6762812af3.png

所以需要自己重寫一個EditText.

public class WiseEditText extends AppCompatEditText {

    private OnKeyListener keyListener;

    public WiseEditText(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
    }

    public WiseEditText(Context context, AttributeSet attrs) {
        super(context, attrs);
    }

    public WiseEditText(Context context) {
        super(context);
    }

    @Nullable
    @Override
    public InputConnection onCreateInputConnection(EditorInfo outAttrs) {
        return new MyInputConnection(super.onCreateInputConnection(outAttrs),true);
    }

    private class MyInputConnection extends InputConnectionWrapper {

        public MyInputConnection(InputConnection target, boolean mutable) {
            super(target, mutable);
        }

        @Override
        public boolean sendKeyEvent(KeyEvent event) {
            if (keyListener != null){
                keyListener.onKey(WiseEditText.this,event.getKeyCode(),event);
            }
            return super.sendKeyEvent(event);
        }

        @Override
        public boolean deleteSurroundingText(int beforeLength, int afterLength) {
            if (beforeLength == 1 && afterLength == 0) {
                // backspace
                return sendKeyEvent(new KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_DEL))
                       /* && sendKeyEvent(new KeyEvent(KeyEvent.ACTION_UP, KeyEvent.KEYCODE_DEL))*/;
            }
            return super.deleteSurroundingText(beforeLength, afterLength);
        }
    }

    //設(shè)置監(jiān)聽回調(diào)
    public void setSoftKeyListener(OnKeyListener listener){
        keyListener = listener;
    }
}

然后在自定義的密碼框中去監(jiān)聽回調(diào):

// 監(jiān)聽驗證碼刪除按鍵
        editText.setSoftKeyListener(new OnKeyListener() {
            @Override
            public boolean onKey(View v, int keyCode, KeyEvent keyEvent) {
                if ((keyCode == KeyEvent.KEYCODE_DEL || keyCode == KeyEvent.KEYCODE_FORWARD_DEL ) && keyEvent.getAction() == KeyEvent.ACTION_DOWN && mCodes.size() > 0) {
                    mCodes.remove(mCodes.size() - 1);
                    showCode();
                    return true;
                }
                return false;
            }
        });
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容