Android自定义TextInputEditText失焦回滚文本并显示末尾省略号的优化方案
现有实现存在的核心问题
- 手动截断逻辑可靠性极差:没有考虑字体非等宽、中英文/特殊符号宽度差异,靠比例算出来的截断长度加上硬编码的5、10、69dp偏移量,适配不同分辨率、不同文本内容时经常出现文字切一半、留大量空白的问题
- 逻辑互相触发连锁刷新:onMeasure、onSizeChanged、setText、onFocusChanged几个方法互相调用,一次焦点变化就会触发3-4次完整布局重绘,输入时容易出现卡顿、界面抖动
- XML属性配置冲突:同时开启
scrollHorizontally="true"和ellipsize="end"是互斥的,系统会优先走横向滚动逻辑,直接忽略省略号截断规则,这也是之前单纯靠XML配置出不来效果的核心原因 - 状态逻辑断层:失焦后只做了文本截断,没有复位滚动位置,依然会停留在用户输入时滚动到的文本末尾位置
- 多余对象开销:每次状态切换都做字符串转码、截取、拼接操作,大文本场景下会产生大量临时字符串对象,拖慢GC效率
优化后的落地方案
核心交互逻辑
完全替代手动算截断的思路,利用系统原生的文本测量与截断能力,在焦点变化时动态切换控件的行为模式:
- 聚焦状态:开启横向滚动能力,保留正常输入体验,全程保存用户输入的完整原始文本,不做任何文本篡改
- 失焦状态:锁定滚动位置并强制拉回文本开头,关闭横向滚动能力,触发系统原生的end省略号截断逻辑,自动适配不同文本的宽度计算
- 重写文本获取方法,保证业务层永远能拿到用户输入的完整内容,不会拿到被截断的带省略号的文本
优化后的自定义控件代码
class CustomTextInputEditText(context: Context, attrs: AttributeSet) : TextInputEditText(context, attrs) { // 仅保存用户输入的完整原始文本 private var originalInputText: CharSequence = "" override fun onFocusChanged(focused: Boolean, direction: Int, previouslyFocusedRect: Rect?) { super.onFocusChanged(focused, direction, previouslyFocusedRect) movementMethod = if (focused) { // 聚焦时:开启横向滚动,支持正常编辑 ScrollingMovementMethod() } else { // 失焦时:先复位滚动位置到文本开头 post { scrollTo(0, 0) } // 关闭滚动逻辑,自动触发系统的ellipsize截断 null } } override fun setText(text: CharSequence?, type: BufferType?) { // 仅保存原始文本,不做额外的字符串处理 originalInputText = text ?: "" super.setText(originalInputText, type) } // 保证返回的文本永远是用户输入的完整内容 override fun getText(): Editable { return Editable.Factory.getInstance().newEditable(originalInputText) } // 暴露方法给业务层获取真实输入值 fun getRawInput(): String = originalInputText.toString() }
修正后的XML配置
去掉所有冲突属性,删除会拦截焦点的无效配置:
<com.xxx.yyy.CustomTextInputEditText android:id="@+id/edt_select_address" android:layout_width="match_parent" android:layout_height="wrap_content" android:singleLine="true" android:maxLines="1" android:ellipsize="end" android:paddingEnd="@dimen/margin_24" android:textCursorDrawable="@null" android:fontFamily="@font/opensans_regular" android:textSize="@dimen/margin_16" android:text="" />
注意:必须删除之前写的
android:focusable="false",否则控件根本无法获取输入焦点,所有焦点状态切换逻辑都不会触发;不需要静态配置android:scrollHorizontally="true",滚动能力会在聚焦时动态开启。
性能与体验提升点
- 所有文本截断逻辑交给系统原生实现,完全替代手动计算宽度、手动截取拼接的逻辑,没有多余的measureText调用和字符串操作,CPU开销大幅降低
- 去掉了方法循环调用的连锁重绘问题,一次焦点切换只做一次属性更新,不会触发多次布局测量,解决输入卡顿、界面抖动问题
- 没有多余的临时字符串对象生成,减少内存占用和GC触发频率
- 失焦时通过post任务复位滚动位置,避免因为布局未完成导致的滚动位置残留问题
- 系统原生的ellipsize逻辑会自动处理不同宽度字符的截断,适配中英文混排、特殊符号等场景,不会出现切字错误的问题
内容的提问来源于stack exchange,提问作者Đức Dương Văn
相关产品推荐
相关产品推荐

