matlab edittext 回车,安卓中的虚拟键盘实现,KeyEvent的事件分发、处理机制。EditText是如何将KeyEvent事件转为字符输入的?...

目录

一、实现一个可以模拟输入的软键盘

一开始,我们的需求是在用户经常使用的部分界面中,增加虚拟软键盘,减少用户对于外接键盘的依赖

a0596ce77b76296fd7b28b3ac5c3fd59.png

如图,在整单改价界面右侧增加了方便用户快捷输入的软键盘,用户不需要使用外接键盘,即可完成常见的商品改价等操作。

那么这个代码逻辑实现起来比较简单,因为业务中有许多类似界面需要使用该软键盘功能,所以我们将它单独封装为一个View:

mView = View.inflate(context, R.layout.res_keypad_view, this)

val map = hashMapOf()

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封装好了,其他界面如何使用呢?

非常简单:

android:layout_width="match_parent"

android:layout_height="wrap_content"

android:orientation="horizontal">

android:layout_width="400dp"

android:layout_height="350dp"

android:orientation="vertical">

<.....keypadview>

android:layout_width="263dp"

android:layout_height="match_parent"

android:layout_marginLeft="10dp"

android:background="@drawable/res_best_white_button_default" />

只需要一个LinearLayout 将原本的业务view和软键盘view包括起来就OK了

好的,那么正当我测试了几个界面没有问题的时候,我把这个软键盘应用在另一个界面时,问题出现了:

二、问题:点击软键盘,没有任何反应,输入框没有填入字符

d05f234388811b984f6e8e5865dd6aa6.png

就是这个界面,我没有发现它与正常界面有何区别,于是只能尝试通过底层源码,来寻找问题原因

那么我首先怀疑是因为dispatchKeyEvent()方法没有正确将按键事件传递给EditText,但是经过调试后,发现事件确实有传递过来

e01f2586749d094868a8ea23acd5be58.png

首先,当我们通过debug模式,查看安卓源码时,记得打开源文件后,在右上角选择和你运行的机器对应的安卓sdk版本,比如我的真机,安卓版本是5.1.1,那么此处我要选择查看api 22 版本的源代码。这样就不会出现断点乱跳,无法定位代码的问题。

4318db3bdb7cc7d933d68afce0cb8a4e.png

断点之后,通过代码逻辑可知,返回0表示没有处理/消耗此事件,那么问题很有可能出现在这个doKeyDown()方法内

04956c988044d0c857dcda26e47e3812.png

在doKeyDown()方法内部,我发现了可疑代码,这段代码判断了,当KeyEvent的按键动作是按下时,使用了类似于数据库事务操作的方式,对其进行编辑

并返回一个布尔值,如果为true,则返回到外部:1,表示该keyEvent被处理/消耗。

5d0eb1e1b2fef0c04a57cdeb3485f949.png

继续深入该方法,发现KeyListener是一个接口,拥有好几个实现类

e3ce6256b3cb54747a89db955a02652a.png

鼠标停留在变量上,可以看到此时,实现类是TextKeyListener

177534cf29dfe94a14463539aeaa1d76.png

跟踪TextKeyListener的onKeyDown方法,最终发现,它实际调用的是QwertyKeyListener的onKeyDown()方法

da6cd549cc08ee4ece55ccd6e80d2be1.png

3f27109937da05bc65040e4818a3b338.png

此时,我发现了一行关键的代码,当我使用小键盘的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.

*

* Returns the Unicode character that the specified key would produce

* when the specified meta bits (see {@link MetaKeyKeyListener})

* were active.

*

* Returns 0 if the key is not one that is used to type Unicode

* characters.

*

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

*

*

* @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等),获得一个字符

当我们传入小键盘的键码时,无法正确获得对应字符,当传入大键盘的键码时,却可以获得字符,这就是问题的原因。

那么,下一个问题来了,

三、为什么在前面经过测试的其他界面中,软键盘却又可以正常录入字符呢?

那么,我们找到可以正常使用软键盘的界面,再重复一遍以上过程,

5ca5073b166a8d772eeb7b9df415b1f3.png

发现这次,代码没有进入TextKeyListener和QwertyKeyListener,而是进入了另一个实现类,NumberKeyListener的onKeyDown方法

9f4b08067d61c2496891e726e03e8458.png

可以发现,此处的区别是,它并没有用上面的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()方法,选择不同的实现类,来实现最终目的

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值