前言
网上开源的是构建同等数量的 EditText,这种存在很多缺陷,主要如下
1、数字 / 字符键盘切换后键盘状态无法保存
2、焦点切换无法判断
3、光标位置无法修正
4、切换过程需要做很多同步工作
5、需要处理聚焦选中区域问题
6、性能差
EditText越多,造成的不确定性问题将越多,因此,在开发中,如果我们自行实现一个纯View的输入框有没有可能呢?比较遗憾的是,Android 层面android.widget.Editor是非公开的类,因此很难去实现一个想要的View。
另一种方案,我们继承TextView,改写TextView的绘制逻辑也是可以。
为什么TextView是可以的呢?
- 第一:TextView 本身可以输入任何文本
- 第二:TextView 绘制方法中使用android.widget.Editor可以辅助keycode->文本转换
- 第三:TextView 提供了光标等各种组件
核心步骤
为了解决上述问题,使用 TextView 实现输入框,这里需要解决的问题是
1、允许 TextView 可编辑输入,这点可以参考EditText的实现
2、重写 onDraw 实现,不实用原有的绘制逻辑。
3、重写光标逻辑,默认的光标逻辑和Editor有很多关联逻辑,而Editor是@hide标注的,因此必须要重写
4、重写长按菜单逻辑,防止弹出剪切、复制、选中等PopWindow弹窗。
5、限制文本长度
代码实现
首先我们要继承TextView或者AppCompatTextView,然后实现下面的操作
变量定义
1 | java复制代码//边框颜色 |
关键状态
禁止复制、粘贴、选中
1 | java复制代码super.setFocusable(true); //支持聚焦 |
绘制逻辑
我们重写onDraw方法,自行绘制View
1 | java复制代码TextPaint paint = getPaint(); |
InsertionHandleView问题
我们上文处理了各种可能出现的选中区域弹窗,然而一个很难处理的弹窗双击后会展示,评论区有同学也贴出来了。主要原因是Editor为了方便EditText选中,在内部使用了InsertionHandleView去展示一个弹窗,但这个弹窗并不是直接addView的,而是通过PopWindow展示的,具体可以参考下面源码。
实际上,掘金Android 客户端也有类似的问题,不过掘金app的实现方式是使用多个EditText实现的,点击的时候就会明显看到这个小雨点,其次还有光标卡顿的问题。
android.widget.Editor.InsertionHandleView
解决方法其实有3种:
第一种是Hack Context,返回一个自定义的WindowManager给PopWindow,不过我们知道InputManagerService 作为 WindowManagerService中的子服务,如果处理不当,可能产生输入法无法输入的问题,另外要Hack WindowManager,显然工作量很大。
第二种是替换:修改InsertionHandleView的背景元素,具体可参考:blog.csdn.net/shi_xin/art… 一文
1 | java复制代码<item name="textSelectHandleLeft">@drawable/text_select_handle_left_material</item> |
这种方式增加了View的可扩展性,自定义View要尽可能避免和xml配置耦合,除非是自定义属性。
第三种是拦截hide方法,在popWindow展示之后,会立即设置一个定时消失的逻辑,这种相对简单,而且View的通用性不受影响,但是也有些不规范,不过目前这个调用还是相当稳定的。
综上,我们选择第三种方案,我这里直接拦截其内部调用postDelay的方法,如果是InsertionHandleView的内部类,且时间为4000秒,直接执行runnable
1 | java复制代码private void hideAfterDelay() { |
下面是解法:
1 | java复制代码@Override |
总结
上面就是本文的核心逻辑,实际上EditText、Button都继承自TextView,因此我们简单的修改就能让其支持输入,主要原因还是TextView复杂的设计和各种Layout的支持,但是这也给TextView带来了性能问题。
这里简单说下TextView性能优化,对于单行文本和非可编辑文本,最好是自行实现,单行文本直接用canvas.drawText绘制,当然多行也是可以的,不过鉴于要支持很多特性,多行文本可以使用StaticLayout去实现,但单行文本尽量自己绘制,也不要使用BoringLayout,因为其存在一些兼容性问题,另外自定义的单行文本不要和TextView同一行布局,因为TextView的计算相对较多,很可能产生对不齐的问题。
本篇全部代码
按照惯例,这里依然提供全部代码,仅供参考,当然,也可以直接使用到项目中,本篇代码在线上已经使用过。
1 | java复制代码public class EditableTextView extends TextView { |
本文转载自: 掘金