项目中使用富文本比较常见了,一行显示多种样式颜色的文本,使用 ClickableSpan 富文本实现在同一个 TextView 中的文本的颜色、大小、背景色等属性的多样化和个性化。
我们也可以使用Html.fromHtml(string)来编写文本格式,需要注意:
1.当string过大,会抛出IOException
2.Html.fromHtml(string)会将string中的’
’和’
’替换成空格,因此我们需要显式的将其替换为html可以识别的’<br>’否则会报IOException
3.android原生仅支持部分html tag标签
使用ClickableSpan会遇到的问题:
用ClickableSpan给TextView中文本设置响应事件,再对TextView设置响应事件,点击ClickableSpan时,会同时触发2个事件。
常见案例:评论回复,点击文本,弹出对话框,点击昵称,进入用户主页(朋友圈评论回复)
原因:
1.查看源码得知,给TextView setMovementMethod中,LinkMovementMethod的onTouchEvent方法,会同时触发自身的onClick事件 ,返回给view进行处理,这时该TextView也消耗了事件,因此会触发2个事件。
2. TextView setMovementMethod方法,默认将以下3个方法设置为true,根据安卓的事件分发机制,TextView也会消耗掉该事件。
setFocusable(true); setClickable(true); setLongClickable(true);
如何解决:
上述例子中,我们可以禁用TextView的点击事件,点击文本时,交给父类去处理,点击昵称,ClickableSpan进入用户主页
1. 重写LinkMovementMethod,将onTouchEvent事件返回为false.
2.TextView不可点击。
setFocusable(false);
setClickable(false);
setLongClickable(false);
问题代码:
点击ClickableSpan,日志输出:
修改后代码:
ClickableMovementMethod修改:
class ClickableMovementMethod : LinkMovementMethod() { companion object { val mInstance by lazy(LazyThreadSafetyMode.NONE){ ClickableMovementMethod() } } 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() var y = event.y.toInt() x -= widget.totalPaddingLeft y -= widget.totalPaddingTop x += widget.scrollX y += widget.scrollY val layout = widget.layout val line = layout.getLineForVertical(y) val off = layout.getOffsetForHorizontal(line, x.toFloat()) val links = buffer.getSpans(off, off, ClickableSpan::class.java) if (links.size != 0) { val link = links[0] if (action == MotionEvent.ACTION_UP) { link.onClick(widget) } else if (action == MotionEvent.ACTION_DOWN) { Selection.setSelection( buffer, buffer.getSpanStart(link), buffer.getSpanEnd(link) ) } return true } else { Selection.removeSelection(buffer) } } return false } }
日志输出:
点击ClickableSpan:
点击文本:
参考链接: