TextView中ClickableSpan和OnClickListener事件冲突

57 阅读1分钟

一段文字中包含@简书 的特殊字符串,该字符串带特殊前景色和特殊点击事件

SpannableStringBuilder style = new SpannableStringBuilder(text);
int textColor = textView.getContext().getResources().getColor(R.color.theme);
int highlightColor = textView.getContext().getResources().getColor(R.color.transparent);
if (onSpanClickCommand != null) {
    //SPAN_EXCLUSIVE_EXCLUSIVE:标识在Span范围内的文本前后输入新的字符时是否把它们也应用这个效果
    style.setSpan(new ClickableSpan() {
        @Override
        public void onClick(@NonNull View widget) {
            onSpanClickCommand.execute();
        }
        @Override
        public void updateDrawState(@NonNull TextPaint ds) {
            super.updateDrawState(ds);
            ds.setColor(textColor);
            ds.setUnderlineText(false);
        }
    }, start, end, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
} else {
    style.setSpan(new ForegroundColorSpan(textColor), start, end, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
}
//设置textView中所有文字为spannable格式
textView.setText(style);
//启用上面为每个字绑定的ClickableSpan
//TextViewSpanClickableMovementMethod里解决了TextView “无ClickableSpan事件” 文案无点击事件BUG
textView.setMovementMethod(TextViewSpanClickableMovementMethod.getInstance());
//去掉点击Span的背景
textView.setHighlightColor(highlightColor);
问题来了:@简书 的点击事件有了,其他文字的点击事件没了
解决方案:

继承LinkMovementMethod,重新onTouchEvent方法,当点击未设置ClickableSpan事件的文字时,super.onTouchEvent中返回的Boolean值为false,此时就可以执行OnClickListener事件
1、可以拿到TextView父控件中的事件,直接执行
2、getInstance方法中传入需要执行的OnClickListener事件,在onTouchEvent中执行

public class TextViewSpanClickableMovementMethod extends LinkMovementMethod {
    private static TextViewSpanClickableMovementMethod instance;

    public static TextViewSpanClickableMovementMethod getInstance() {
        if (instance == null) {
            instance = new TextViewSpanClickableMovementMethod();
        }
        return instance;
    }

    @Override
    public boolean onTouchEvent(TextView widget, Spannable buffer, MotionEvent event) {
        boolean b = super.onTouchEvent(widget, buffer, event);
        //当TextView中某段文字没有设置ClickableSpan事件的时候,b返回false,此时点击这段文字,响应父类事件
        if (!b && event.getAction() == MotionEvent.ACTION_UP) {
            ViewParent parent = widget.getParent();
            if (parent instanceof ViewGroup) {
                return ((ViewGroup) parent).performClick();
            }
        }
        return b;
    }
}