目錄
一、實作一個可以模擬輸入的軟鍵盤
二、問題:點擊軟鍵盤,沒有任何反應,輸入框沒有填入字符
原因:傳入小鍵盤鍵碼,和大鍵盤鍵碼,得到的結果不一致
三、為什么在前面經過測驗的其他界面中,軟鍵盤卻又可以正常錄入字符呢?
原因:使用了不同的KeyListener實作類
安卓KeyEvent的處理機制總結:
一、實作一個可以模擬輸入的軟鍵盤
一開始,我們的需求是在用戶經常使用的部分界面中,增加虛擬軟鍵盤,減少用戶對于外接鍵盤的依賴

如圖,在整單改價界面右側增加了方便用戶快捷輸入的軟鍵盤,用戶不需要使用外接鍵盤,即可完成常見的商品改價等操作,
那么這個代碼邏輯實作起來比較簡單,因為業務中有許多類似界面需要使用該軟鍵盤功能,所以我們將它單獨封裝為一個View:
mView = View.inflate(context, R.layout.res_keypad_view, this)
val map = hashMapOf<View, Int>()
map[tv_num_1] = KeyEvent.KEYCODE_NUMPAD_1
map[tv_num_2] = KeyEvent.KEYCODE_NUMPAD_2
map[tv_num_3] = KeyEvent.KEYCODE_NUMPAD_3
map[tv_num_4] = KeyEvent.KEYCODE_NUMPAD_4
map[tv_num_5] = KeyEvent.KEYCODE_NUMPAD_5
map[tv_num_6] = KeyEvent.KEYCODE_NUMPAD_6
map[tv_num_7] = KeyEvent.KEYCODE_NUMPAD_7
map[tv_num_8] = KeyEvent.KEYCODE_NUMPAD_8
map[tv_num_9] = KeyEvent.KEYCODE_NUMPAD_9
map[tv_num_0] = KeyEvent.KEYCODE_NUMPAD_0
map[tv_num_dot] = KeyEvent.KEYCODE_NUMPAD_DOT
map[tv_num_del] = KeyEvent.KEYCODE_DEL
map.iterator().forEach { item ->
item.key.setOnClickListener {
(mView.parent as View).dispatchKeyEvent(KeyEvent(KeyEvent.ACTION_DOWN, item.value))
}
}
做好布局后,在代碼中建立每個view與它所代表的鍵碼的對應關系,使用map持有它們,最后遍歷map集合,給每個view設定點擊事件,觸發點擊事件時,我們構造一個KeyEvent物件,然后找到當前view(即軟鍵盤view)的父view,呼叫其dispatchKeyEvent()方法,向其分發鍵盤事件,然后依靠安卓自身的事件處理機制,該事件就能被正確的傳遞給需要它的EditText,
利用系統自身的事件傳遞機制,去幫我們實作將KeyEvent轉化為字符,輸入進EditText,是再好不過的,我們就不需要考慮直接操作EditText可能帶來的各種問題,
那么,軟鍵盤view封裝好了,其他界面如何使用呢?
非常簡單:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<LinearLayout
android:layout_width="400dp"
android:layout_height="350dp"
android:orientation="vertical">
<!-- 此處為原本的業務View -->
</LinearLayout>
<.....KeypadView
android:layout_width="263dp"
android:layout_height="match_parent"
android:layout_marginLeft="10dp"
android:background="@drawable/res_best_white_button_default" />
</LinearLayout>
只需要一個LinearLayout 將原本的業務view和軟鍵盤view包括起來就OK了
好的,那么正當我測驗了幾個界面沒有問題的時候,我把這個軟鍵盤應用在另一個界面時,問題出現了:
二、問題:點擊軟鍵盤,沒有任何反應,輸入框沒有填入字符

就是這個界面,我沒有發現它與正常界面有何區別,于是只能嘗試通過底層原始碼,來尋找問題原因
那么我首先懷疑是因為dispatchKeyEvent()方法沒有正確將按鍵事件傳遞給EditText,但是經過除錯后,發現事件確實有傳遞過來

首先,當我們通過debug模式,查看安卓原始碼時,記得打開源檔案后,在右上角選擇和你運行的機器對應的安卓sdk版本,比如我的真機,安卓版本是5.1.1,那么此處我要選擇查看api 22 版本的源代碼,這樣就不會出現斷點亂跳,無法定位代碼的問題,

斷點之后,通過代碼邏輯可知,回傳0表示沒有處理/消耗此事件,那么問題很有可能出現在這個doKeyDown()方法內

在doKeyDown()方法內部,我發現了可疑代碼,這段代碼判斷了,當KeyEvent的按鍵動作是按下時,使用了類似于資料庫事務操作的方式,對其進行編輯
并回傳一個布林值,如果為true,則回傳到外部:1,表示該keyEvent被處理/消耗,

繼續深入該方法,發現KeyListener是一個介面,擁有好幾個實作類

滑鼠停留在變數上,可以看到此時,實作類是TextKeyListener

跟蹤TextKeyListener的onKeyDown方法,最終發現,它實際呼叫的是QwertyKeyListener的onKeyDown()方法


此時,我發現了一行關鍵的代碼,當我使用小鍵盤的1的code(KEYCODE_NUMPAD_1),和大鍵盤的1的code(KEYCODE_1),得到了截然不同的結果
接著看KeyEvent的getUnicodeChar()方法
/**
* Gets the Unicode character generated by the specified key and meta
* key state combination.
* <p>
* Returns the Unicode character that the specified key would produce
* when the specified meta bits (see {@link MetaKeyKeyListener})
* were active.
* </p><p>
* Returns 0 if the key is not one that is used to type Unicode
* characters.
* </p><p>
* If the return value has bit {@link KeyCharacterMap#COMBINING_ACCENT} set, the
* key is a "dead key" that should be combined with another to
* actually produce a character -- see {@link KeyCharacterMap#getDeadChar} --
* after masking with {@link KeyCharacterMap#COMBINING_ACCENT_MASK}.
* </p>
*
* @param metaState The meta key modifier state.
* @return The associated character or combining accent, or 0 if none.
*/
public int getUnicodeChar(int metaState) {
return getKeyCharacterMap().get(mKeyCode, metaState);
}
原因:傳入小鍵盤鍵碼,和大鍵盤鍵碼,得到的結果不一致
根據注釋,可以得知該方法即為將按鍵事件,轉為字符的核心方法,根據keyCode和鍵盤控制鍵(如Ctrl,Shift,NumLock等),獲得一個字符
當我們傳入小鍵盤的鍵碼時,無法正確獲得對應字符,當傳入大鍵盤的鍵碼時,卻可以獲得字符,這就是問題的原因,
那么,下一個問題來了,
三、為什么在前面經過測驗的其他界面中,軟鍵盤卻又可以正常錄入字符呢?
那么,我們找到可以正常使用軟鍵盤的界面,再重復一遍以上程序,

發現這次,代碼沒有進入TextKeyListener和QwertyKeyListener,而是進入了另一個實作類,NumberKeyListener的onKeyDown方法

可以發現,此處的區別是,它并沒有用上面的getUnicodeChar()方法去轉換字符,而是通過自己的lookup()方法,轉換字符
原因:使用了不同的KeyListener實作類
那么,結合類名和現象,我們可以推測出:
TextKeyListener和QwertyKeyListener,他們的字符轉換功能,不支持小鍵盤的鍵碼(即虛擬鍵盤失效,有問題的界面)
NumberKeyListener的字符轉換功能,支持小鍵盤鍵碼(即前面測驗過,功能ok的界面)
找到原因后,修復的辦法也顯而易見,只要讓我們存在問題的界面內的EditText,使用NumberKeyListener做字符轉換即可
使用EditText的 setInputType(EditorInfo.TYPE_CLASS_NUMBER)
將其設定為只能輸入數字即可
但是,突然又想到,這個界面是需要支持輸入商品編碼的,而我們的部分商品編碼是以字母Z開頭的,所以設定為只能輸入數字,將會影響業務邏輯
此時,看到了最開始我們發射事件的代碼,
map[tv_num_1] = KeyEvent.KEYCODE_NUMPAD_1
map[tv_num_2] = KeyEvent.KEYCODE_NUMPAD_2
map[tv_num_3] = KeyEvent.KEYCODE_NUMPAD_3
map[tv_num_4] = KeyEvent.KEYCODE_NUMPAD_4
map[tv_num_5] = KeyEvent.KEYCODE_NUMPAD_5
map[tv_num_6] = KeyEvent.KEYCODE_NUMPAD_6
map[tv_num_7] = KeyEvent.KEYCODE_NUMPAD_7
map[tv_num_8] = KeyEvent.KEYCODE_NUMPAD_8
map[tv_num_9] = KeyEvent.KEYCODE_NUMPAD_9
map[tv_num_0] = KeyEvent.KEYCODE_NUMPAD_0
map[tv_num_dot] = KeyEvent.KEYCODE_NUMPAD_DOT
里面的鍵碼全部是用的小鍵盤鍵碼,直接把它們都改為大鍵盤鍵碼,問題不就都迎刃而解了嗎
改來改去,最后發現問題的原因只是最初發射事件的源頭,,
哈哈,最后針對安卓中的鍵盤事件處理機制,做一下總結
安卓KeyEvent的處理機制總結:
- 如果需要做虛擬鍵盤,模擬鍵盤輸入時,可以借助安卓本身的KeyEvent機制,自己構造KeyEvent物件,使用dispatchKeyEvent()方法將事件分發給父View,剩下的都可以交給系統自行處理~
- 如果出現事件無效的問題,檢查自己構造的鍵碼是否正確,切換大/小鍵盤鍵碼進行嘗試
- 將KeyEvent事件轉換為字符輸入的作業,是由KeyListener的實作類完成的
- KeyListener有很多實作類,每個實作類的職責不同,我們可以通過EditText的setInputType()方法,選擇不同的實作類,來實作最終目的
轉載請註明出處,本文鏈接:https://www.uj5u.com/yidong/245705.html
標籤:其他
上一篇:unity寫的一個轉盤
下一篇:qq-jsp登錄
