Android:多行TextView设置SpannableString后省略失效及滚动异常
解决TextView显示SpannableString时省略号不显示+触摸自动滚动的问题
我之前也碰到过一模一样的问题,这大多是SpannableString的特性和TextView的省略号逻辑、触摸事件处理之间的冲突导致的,给你几个实测有效的解决办法:
1. 先检查并修正TextView的核心布局属性
首先确保你的TextView配置了正确的换行和省略相关属性,完整的布局代码应该包含这些关键项:
<TextView android:id="@+id/text" android:layout_width="match_parent" android:layout_height="wrap_content" android:maxLines="2" android:ellipsize="end" android:singleLine="false" android:scrollHorizontally="false" android:textIsSelectable="false" />
这里要注意几个细节:
- 必须明确指定
android:ellipsize="end"(或者你需要的middle/start),不能省略 - 绝对不能同时开
android:singleLine="true",它会直接覆盖maxLines的设置 android:scrollHorizontally="false"是关键,多行文本时如果开了这个属性,省略号逻辑会失效
2. 正确构造SpannableString,避免破坏布局测量
有时候直接设置SpannableString后,TextView的布局测量会出问题,你可以用SpannableStringBuilder来构建文本,并且确保不要给文本设置会大幅改变布局的Span(比如超大字号的Span)。如果是在代码里设置文本,建议用post()确保TextView完成布局后再刷新:
val spannableContent = SpannableStringBuilder("你的超长文本内容,包含需要设置样式的部分...") // 给指定位置添加Span,比如前景色、下划线等 spannableContent.setSpan( ForegroundColorSpan(Color.RED), 5, 10, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE ) textView.text = spannableContent textView.post { textView.invalidate() }
用post()延迟刷新,能让TextView的省略号逻辑正常触发。
3. 解决触摸自动滚动到末尾的问题
这个问题基本是因为TextView默认的MovementMethod或者textIsSelectable属性导致的:
- 如果不需要选中文本:直接在布局里设置
android:textIsSelectable="false"就可以解决 - 如果需要选中文本:可以自定义一个
MovementMethod来阻止自动滚动行为:
class NoScrollMovementMethod : LinkMovementMethod() { override fun onTouchEvent(widget: TextView, buffer: Spannable, event: MotionEvent): Boolean { val action = event.action if (action == MotionEvent.ACTION_UP || action == MotionEvent.ACTION_DOWN) { // 计算触摸位置,但不触发默认滚动 var x = event.x.toInt() - widget.totalPaddingLeft + widget.scrollX var y = event.y.toInt() - widget.totalPaddingTop + widget.scrollY val layout = widget.layout val line = layout.getLineForVertical(y) layout.getOffsetForHorizontal(line, x.toFloat()) if (action == MotionEvent.ACTION_DOWN) { widget.highlightColor = Color.TRANSPARENT return true } } return super.onTouchEvent(widget, buffer, event) } companion object { private var instance: NoScrollMovementMethod? = null fun getInstance(): NoScrollMovementMethod { if (instance == null) instance = NoScrollMovementMethod() return instance!! } } }
然后在代码里给TextView设置这个自定义的MovementMethod:
textView.movementMethod = NoScrollMovementMethod.getInstance()
4. 极端情况:自定义TextView手动处理省略号
如果上面的方法都没效果,那可以考虑自定义TextView,重写onMeasure方法,手动判断文本是否超过指定行数,然后给SpannableString添加省略号的Span:
class SpannableEllipsizeTextView @JvmOverloads constructor( context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0 ) : TextView(context, attrs, defStyleAttr) { override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) { super.onMeasure(widthMeasureSpec, heightMeasureSpec) val layout = layout ?: return if (layout.lineCount > maxLines) { val text = text as? Spannable ?: return val endIndex = layout.getLineEnd(maxLines - 1) if (endIndex < text.length) { text.setSpan( ForegroundColorSpan(currentTextColor), endIndex - 3, endIndex, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE ) text.replace(endIndex - 3, text.length, "...") } } } }
直接在布局里用这个自定义TextView即可。
内容的提问来源于stack exchange,提问作者Samantha Withanage
相关产品推荐
相关产品推荐

