TextView常见问题解决方案!

/ 0评 / 2

1、ClickableSpan与TextView点击冲突

在我们使用ClickableSpan的时候,需要配合MovementMethod才能实现,不过往往会导致整个TextView的点击事件都被拦截

movementMethod = LinkMovementMethod.getInstance()

解决方案,不使用LinkMovementMethod,手动解析处理

var onItemClick: (() -> Unit)? = null

setOnTouchListener { v, event ->
    when (event.actionMasked) {
        MotionEvent.ACTION_UP -> {
            val tv = v as? TextView ?: return@setOnTouchListener false
            val spanned = (tv.text as? Spanned) ?: return@setOnTouchListener false
            val x = event.x - tv.totalPaddingLeft + tv.scrollX
            val y = event.y - tv.totalPaddingTop + tv.scrollY
            val offset =
                tv.layout.getOffsetForHorizontal(
                    tv.layout.getLineForVertical(y.toInt()),
                    x
                )
            if (spanned.getSpans(offset, offset, ClickableSpan::class.java)
                    .getOrNull(0) != null
            ) {
                spanned.getSpans(offset, offset, ClickableSpan::class.java).getOrNull(0)
                    ?.onClick(v)
            } else {
                onItemClick?.invoke()
            }
        }
    }
    true
}

2、TextView点击展开效果

//使用StaticLayout测量文字宽度
val layout = StaticLayout(
    originalText, paint, measuredWidth,
    Layout.Alignment.ALIGN_NORMAL, lineSpacingMultiplier, lineSpacingExtra, false
)
val span = SpannableStringBuilder()
if (layout.lineCount > maxLines) {
    val expandTextWithDot = "... $expandText"
    //获取最大一行的最后一个字符的位置
    val lastVisibleLineEndIndex = layout.getLineVisibleEnd(maxLines - 1)
    val content =
        originalText.subSequence(
            0,
            lastVisibleLineEndIndex - expandTextWithDot.length - 1
        )
    span.append(content)
    span.append("... ")
    span.append(expandText)
    span.setSpan(
        MoreClickableSpan(moreColor) { toggle() },
        span.length - expandText.length,
        span.length,
        Spanned.SPAN_INCLUSIVE_EXCLUSIVE
    )
}

完整代码如下,只有点击展开,没有关闭

class ExpandableTextView : AppCompatTextView {

    private val expandText = context.getString(R.string.string_key_2067)
    private val moreColor = context.resources.getColor(R.color.sui_color_link)

    var onItemClick: (() -> Unit)? = null

    constructor(context: Context) : this(context, null)
    constructor(context: Context, attrs: AttributeSet?) : this(context, attrs, 0)
    constructor(context: Context, attrs: AttributeSet?, defStyleAttr: Int) : super(
        context,
        attrs,
        defStyleAttr
    )

    init {
        setOnTouchListener { v, event ->
            when (event.actionMasked) {
                MotionEvent.ACTION_UP -> {
                    val tv = v as? TextView ?: return@setOnTouchListener false
                    val spanned = (tv.text as? Spanned) ?: return@setOnTouchListener false
                    val x = event.x - tv.totalPaddingLeft + tv.scrollX
                    val y = event.y - tv.totalPaddingTop + tv.scrollY
                    val offset =
                        tv.layout.getOffsetForHorizontal(
                            tv.layout.getLineForVertical(y.toInt()),
                            x
                        )
                    if (spanned.getSpans(offset, offset, ClickableSpan::class.java)
                            .getOrNull(0) != null
                    ) {
                        spanned.getSpans(offset, offset, ClickableSpan::class.java).getOrNull(0)
                            ?.onClick(v)
                    } else {
                        onItemClick?.invoke()
                    }
                }
            }
            true
        }
    }

    private var originalText: CharSequence = ""
    var isExpanded = false

    /**
     * 展开/收起文本
     */
    private fun toggle() {
        isExpanded = !isExpanded
        setText()
    }

    /**
     * 设置文本内容
     */
    fun setTextSuper(content: CharSequence) {
        originalText = content
        setText()
    }

    /**
     * 根据是否展开设置文本内容
     */
    private fun setText() {
        // 如果文本已经展开,直接显示原始内容和收起按钮
        if (isExpanded) {
            maxLines = Integer.MAX_VALUE
            text = SpannableStringBuilder(originalText)
        } else {
            val layout = StaticLayout(
                originalText, paint, measuredWidth,
                Layout.Alignment.ALIGN_NORMAL, lineSpacingMultiplier, lineSpacingExtra, false
            )
            val span = SpannableStringBuilder()
            if (layout.lineCount > maxLines) {
                val expandTextWithDot = "... $expandText"
                val lastVisibleLineEndIndex = layout.getLineVisibleEnd(maxLines - 1)
                val content =
                    originalText.subSequence(
                        0,
                        lastVisibleLineEndIndex - expandTextWithDot.length - 1
                    )
                span.append(content)
                span.append("... ")
                span.append(expandText)
                span.setSpan(
                    MoreClickableSpan(moreColor) { toggle() },
                    span.length - expandText.length,
                    span.length,
                    Spanned.SPAN_INCLUSIVE_EXCLUSIVE
                )
            } else {
                span.append(originalText)
            }
            text = span
        }
    }

    class MoreClickableSpan(private val moreColor: Int, private val listener: () -> Unit) :
        ClickableSpan() {

        override fun onClick(widget: View) {
            listener.invoke()
        }

        override fun updateDrawState(ds: TextPaint) {
            ds.color = moreColor
            ds.isUnderlineText = false // 取消下划线
        }
    }
}

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注