今天测试提了一个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.
* <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);
}
注释的大概意思是这个监听器是用于监听实体键的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 。其原理图如下:
所以需要自己重写一个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;
}
});