android 按鍵監聽及鍵盤事件流(無法監聽洗掉鍵)
最近在做一個密碼按鍵輸入功能時需要對每次按鍵進行一些處理,于是使用了 OnKeyListener 介面監聽,對于正常文本格式的輸入按鍵事件都能監聽到,但是一旦修改 EditText 的輸入型別為 NumbberPassword(android:inputType="numberPassword") 則無法監聽到鍵盤的洗掉按鈕事件,
于是查閱資料:
一般使用 OnKeyListener 介面監聽按鍵事件如下:
editText.setOnKeyListener(new View.OnKeyListener() {
@Override
public boolean onKey(View v, int keyCode, KeyEvent event) {
if(event.getAction() == KeyEvent.ACTION_DOWN) {
switch (keyCode) {
case KeyEvent.KEYCODE_DEL:
// 處理相關退格鍵行為
return true;
...
}
}
return false;
}
});
上面這個這個方案在大多數情況下都沒有問題,但是當使用 android:inputType="numberPassword" 時事件并未得到回應,于是翻看了關于 OnKeyListener 的注釋:
/**
* 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.
*/
boolean onKey(View v, int keyCode, KeyEvent event);
}
類注釋大概的意思是:硬體按鍵會一定會回呼這個介面,這僅對硬體鍵盤有用,軟體輸入法沒有義務觸發此偵聽器,
方法的注釋大概意思是:硬體的key會派發到這里,但軟體鍵盤中的按鍵通常不會觸發此方法,盡管某些情況下可能會選擇這樣做,不要假設軟體輸入法必須基于密鑰,即使是這樣,它也可能以與您預期不同的方式使用按鍵,因此無法可靠地捕獲軟輸入按鍵,(意思就是這個監聽對軟鍵盤來說并不可靠),既然不可靠那么通過什么方式是谷歌推薦的呢,通過查閱資料得知,
InputConnection 介面
/**
* The InputConnection interface is the communication channel from an
* {@link InputMethod} back to the application that is receiving its
* input. It is used to perform such things as reading text around the
* cursor, committing text to the text box, and sending raw key events
* to the application.
....
*/
public interface InputConnection {
...
}
從上面注釋得知:InputConnection介面是從{@link InputMethod}回傳到正在接收其輸入的應用程式的通信通道,它用于執行諸如讀取游標周圍的文本,將文本提交到文本框以及將原始鍵事件發送到應用程式之類的事情,
事實上谷歌的鍵盤輸入流式這樣完成的:
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();
那么 InputConnection 如何與 EditText 建立關聯的呢?
實際上在EditText和輸入法建立連接的時候,EditText的
onCreateInputConnection()方法會被觸發:
/**
* Create a new InputConnection for an InputMethod to interact
* with the view. The default implementation returns null, since it doesn't
* support input methods. You can override this to implement such support.
* This is only needed for views that take focus and text input.
*
* <p>When implementing this, you probably also want to implement
* {@link #onCheckIsTextEditor()} to indicate you will return a
* non-null InputConnection.</p>
*
* <p>Also, take good care to fill in the {@link android.view.inputmethod.EditorInfo}
* object correctly and in its entirety, so that the connected IME can rely
* on its values. For example, {@link android.view.inputmethod.EditorInfo#initialSelStart}
* and {@link android.view.inputmethod.EditorInfo#initialSelEnd} members
* must be filled in with the correct cursor position for IMEs to work correctly
* with your application.</p>
*/
public InputConnection onCreateInputConnection(EditorInfo outAttrs) {
return null;
}
注釋只貼了核心部分大概意思就是:為InputMethod創建一個新的InputConnection以便與視圖進行互動,默認實作回傳null,因為它不支持輸入法,您可以覆寫它以實作這種支持,僅對于具有焦點和文本輸入的視圖才需要,
在實作此功能時,您可能還希望實作onCheckIsTextEditor()來指示您將回傳非null的InputConnection,
另外,請務必正確且完整地填寫EditorInfo物件,以使連接的IME可以依賴其值,例如,必須使用正確的游標位置填充initialSelStart和initialSelEnd成員,IME才能正確地與您的應用程式一起使用,
也就是說我們只需要實作這個方法并給一個實作介面的回傳我們就可以接管鍵盤輸入了,這個方法是 View 的方法,擴展下想象力,就是任何View都可以去回應按鍵的,那這里我們就可以直接使用了么?并不能因為介面并沒有提供常規處理,如果完全自己實作,我們需要完成其他按鍵相關處理,作業量仍舊巨大,那么EditText具備這個功能那么應該也是有實作的吧,實時上是的,在
TextView中就提供了EditableInputConnection類來處理輸入,但是他是 hide 的無法被繼承,可能出于安全角度考慮,所以就沒有辦法了么?其實google為我們提供了一個類InputConnectionWrapper一個默認代理類,完成了大部分常規的操作,我們可以繼承這個類來針對自己想要的部分實作替換,
/**
* <p>Wrapper class for proxying calls to another InputConnection. Subclass and have fun!
*/
public class InputConnectionWrapper implements InputConnection {
...
}
注釋解釋:包裝器類,用于代理對另一個InputConnection的呼叫,子類,玩得開心!(google工程師還是很幽默的)
到這里我們就可以通過實作這個類來完成鍵盤的攔截監聽了,
/**
* 始終從尾部輸入的編輯文本控制元件
*/
public class TailInputEditText extends AppCompatEditText {
public TailInputEditText(Context context) {
this(context, null);
}
public TailInputEditText(Context context, AttributeSet attrs) {
this(context, attrs, android.R.attr.editTextStyle);
}
public TailInputEditText(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
@Override
protected void onSelectionChanged(int selStart, int selEnd) {
super.onSelectionChanged(selStart, selEnd);
if (selStart == selEnd){//防止不能多選
if(getText() == null){//判空,防止出現空指標
setSelection(0);
}else {
setSelection(getText().length()); // 保證游標始終在最后面
// setSelection(0, getText().length());
}
}
}
@Override
public boolean onKeyDown(int keyCode, KeyEvent event) {
// 其他按鍵事件回應
return super.onKeyDown(keyCode, event);
}
@Override
public InputConnection onCreateInputConnection(EditorInfo outAttrs) {
// 回傳自己的實作
return new BackspaceInputConnection(super.onCreateInputConnection(outAttrs), true);
}
private class BackspaceInputConnection extends InputConnectionWrapper {
public BackspaceInputConnection(InputConnection target, boolean mutable) {
super(target, mutable);
}
/**
* 當軟鍵盤洗掉文本之前,會呼叫這個方法通知輸入框,我們可以重寫這個方法并判斷是否要攔截這個洗掉事件,
* 在谷歌輸入法上,點擊退格鍵的時候不會呼叫{@link #sendKeyEvent(KeyEvent event)},
* 而是直接回呼這個方法,所以也要在這個方法上做攔截;
* */
@Override
public boolean deleteSurroundingText(int beforeLength, int afterLength) {
// 做你想做的是攔截他
return super.deleteSurroundingText(beforeLength, afterLength);
}
}
}
以上就是一個包含了攔截器并與控制元件關聯的實作,當然你也可以不用內部類來完成,我只是簡單的描述一下,
到這里鍵盤事件的攔截問題告一小段落,有什么其他的想法可以留言討論,
轉載請註明出處,本文鏈接:https://www.uj5u.com/yidong/246059.html
標籤:Android

