1.EditText作为一个比较成熟的View,在Android的应用开发中得到极为广泛的使用。在某些特殊情况下,我们可能需要定制EditText的输入内容,
只允许指定功能的输入,例如输入一个”ddd:mm:ssssss“格式的度分秒角度。
一些错误的思路有:
(1)OnKeyListener,实现EditText的OnKeyListener并不能拦截EditText的输入,OnKeyListener只能够监听到一些按物理键事件,例如菜单键、返回键、音量键等,
而输入法作为一个单独的进程,EditText与输入法的通讯显然是OnKeyListener所无能为力的;
(2)TextWatcher,相信很多同志做这个功能的第一反应都是使用TextWatcher,因为TextWatcher可以监听到EditText输入前后内容的变化,但那也只是监听到而已
,它并没有办法真正拦截到输入,而只是在某一字符输入发生后,对EditText做一个事后的处理。典型的例子如下,假设我监听到了”s“的输入,但实际上我是不允许”s“输入的,那
么我在TextWatcher中监听到”s“输入了以后,重新设置EditText的内容。但这样做的恶果就是重设EditText的内容后,又会回调TextWatcher的那几个方法,造成死循环。当然您可以
设置一个标志位来停止这个循环,但麻烦不止于此。因为你是先让字符输入,然后再手动把它删除,那么这其中涉及到的光标位置调整,将无穷无尽……
2.讲述了上面两种典型的错误之后(事实上也是LZ曾经犯过的),介绍一下LZ对于真正EidtText输入拦截的实现。
既然系统给的接口都无法实现,那么势必要通过源码来解决问题了。EditText是继承与TextView的,TextView的源码量非常大,一个个函数看
肯定不现实,那么按关键字搜吧,搜索”input“可以查到”onCreateInputConnection“这个方法 ,字面意思创建与输入法的通信,返回的对象是InputConnection,
很有可能就是它了,InputConnectionWrapper 实现了InputConnection, 而这个类的实现方法中有两个极为可靠的方法,commitText, sendKeyEvent,字面意思提交文本
和按键事件。看到这里,想必思路也差不多了:
(1)重载onCreateInputConnection方法,它需要返回一个InputConnection对象;
(2)继承于InputConnectionWrapper, 实现自己的InputConnection 并且在onCreateInputConnection中返回。
(3)在自定义的InputConnectionWrapper类中,实现输入法输入和按键时间的拦截。
(4)拦截条件:在commitText方法中,如果执行父类的 commitText(即super.commitText(text, newCursorPosition))那么表示不拦截,如果返回false则表示拦截,
输入法的字符串则无法传送到EditText。在sendKeyEvent中,如果执行父类的sendKeyEvent(即super.sendKeyEvent(event))那么表示不拦截,如果返回false表示拦截。
3.至此,EditText的定制输入的最总要环节,拦截EditText与输入法的通讯已经成功实现,接下来的就是根据你的定制规则去重载你的commitText和sendKeyEvent方法吧。
几个重要的方法,
设置光标位置:setSelection(cursor - 1, cursor -1);
删除光标坐标0(m)个到光标右边1(n)个字符:deleteSurroundingText(0, 1);
此外,不同的输入法的KeyEvent方法可能有些不同,你需要查看SoftKeyboard示例源码来对你的应用进行调试,以使它能够兼容主流的输入法。