TextView之富文本

项目中使用富文本比较常见了,一行显示多种样式颜色的文本,使用 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
    }


}
View Code
 
日志输出:
 点击ClickableSpan:

点击文本:

参考链接:
原文地址:https://www.cnblogs.com/fangg/p/12752623.html