EditText的输入类型为NumbberPassword时,小米数字软键盘无法监听到键盘的删除按钮事件

今天测试提了一个bug,在小米手机上,就是一个自定义的密码框,输错密码,想要删除,按软键盘的删除键,却发现没有反应,但是在其他的手机上却可以删除。这一听就头大,很明显的系统不同造成的。但是bug提了,能怎么办,使出独门绝学百度大法。

一般正常的我们监听软键盘的回调就好了。如下:

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

但是在小米手机上这种方式不起效。经过百度查的,有些文章也说在谷歌输入法上也不其效果。

/**
     * 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.
         * 

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

注释的大概意思是这个监听器是用于监听实体键的key event的,虽然输入法也可以发出key event,但是这种事是看缘分的。比如搜狗输入法就是基于keyEvent和EditText交互的,但谷歌输入法就不会发出keyEvent来告知EditText有输入事件,所以用这个监听器来监听软键盘的输入和点击事件是不靠谱的。

谷歌输入法是通过InputConnection类,InputConnection 是输入法和应用内View(通常是EditText)交互的通道,输入法的文本输入和删改事件,包括key event事件都是通过InputConnection发送给EditText。

InputConnection有几个关键方法,通过重写这几个方法,我们基本可以拦截软键盘的所有输入和点击事件:

//当输入法输入了字符,包括表情,字母、文字、数字和符号等内容,会回调该方法
public boolean commitText(CharSequence text, int newCursorPosition) 

//当有按键输入时,该方法会被回调。比如点击退格键时,搜狗输入法应该就是通过调用该方法,
//发送keyEvent的,但谷歌输入法却不会调用该方法,而是调用下面的deleteSurroundingText()方法。  
public boolean sendKeyEvent(KeyEvent event);   

//当有文本删除操作时(剪切,点击退格键),会触发该方法 
public boolean deleteSurroundingText(int beforeLength, int afterLength) 

//结束组合文本输入的时候,回调该方法
public boolean finishComposingText();

当输入法要和指定View建立连接的时候,系统会通过该方法返回一个InputConnection 实例给输入法。所以我们要复写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);
        }
    }

    //设置监听回调
    public void setSoftKeyListener(OnKeyListener listener){
        keyListener = listener;
    }
}

然后在自定义的密码框中去监听回调:

// 监听验证码删除按键
        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;
            }
        });

你可能感兴趣的:(EditText的输入类型为NumbberPassword时,小米数字软键盘无法监听到键盘的删除按钮事件)