ClickableSpan事件和View.onClick()事件冲突

Posted 汤米粥

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了ClickableSpan事件和View.onClick()事件冲突相关的知识,希望对你有一定的参考价值。

一、概述 
需求: 
 
 如下图的一行文本中,有部分文字可点击,执行操作A;其他的文本也可点击,执行操作B; 
 
效果如下图:  
布局文件 
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
              android:orientation="vertical"
              android:id="@+id/ll_root"
              android:layout_width="match_parent"
              android:layout_height="match_parent">

    <TextView
        android:id="@+id/tv_content"
        android:layout_width="match_parent"
        android:padding="15dp"
        android:layout_height="wrap_content"/>

    <View
        android:layout_width="match_parent"
        android:background="#f3f3f3"
        android:layout_height="5dp"/>
</LinearLayout>
 
 
二、问题 
布局文件中有两个控件:LinearLayout 和 TextView,添加事件如下(富文本事件必须添加): 
事件富文本事件TextView点击事件LinearLayout点击事件添加富文本点击事件 和 TextView点击事件YYN添加富文本点击事件 和 LinearLayout点击事件YNN添加富文本点击事件 、TextView点击事件 和 LinearLayout点击事件YYN
由上表可知: 
添加富文本点击事件 和 TextView点击事件时,点击富文本时,会触发 富文本的点击事件 和 TextView的点击事件;添加富文本点击事件 和 LinearLayout点击事件时,富文本的点击事件会拦截 TextView的父容器(LinearLayout)的点击事件; 
 
三、代码 
public class SpannableStringActivity extends AppCompatActivity {

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_spannable);

        LinearLayout llRoot = (LinearLayout) findViewById(R.id.ll_root);
        TextView tvContent = (TextView) findViewById(R.id.tv_content);
        addSpanClick(tvContent, "我是文本我是文本我是文本我是文本我是文本我是文本我是文本我是文本");
        llRoot.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                Toast.makeText(SpannableStringActivity.this, "文本被点击了", Toast.LENGTH_SHORT).show();
            }
        });
        // 不能使用 tvContent 的点击事件,否则点击“我是前缀”时,也会触发tvContent的点击事件;
//        tvContent .setOnClickListener(new View.OnClickListener() {
//            @Override
//            public void onClick(View v) {
//                Toast.makeText(SpannableStringActivity.this, "TextView被点击了", Toast.LENGTH_SHORT).show();
//            }
//        });
    }

    // 添加ClickableSpan事件
    private void addSpanClick(TextView tv, String content) {
        SpannableString spanString = new SpannableString("我是前缀:");
        ForegroundColorSpan span = new ForegroundColorSpan(Color.BLUE);
        spanString.setSpan(span, 0, 5, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
        spanString.setSpan(new ClickableSpan() {
            @Override
            public void onClick(View widget) {
                Toast.makeText(SpannableStringActivity.this, "我是前缀", Toast.LENGTH_SHORT).show();
            }

            @Override
            public void updateDrawState(TextPaint ds) {
                super.updateDrawState(ds);
                ds.setAntiAlias(true);
                ds.setUnderlineText(false);
            }
        }, 0, 5 , Spanned.SPAN_INCLUSIVE_EXCLUSIVE);
        // 这里需要添加这一行代码,否则富文本的点击事件不生效;
        tv.setMovementMethod(MyLinkedMovementMethod.getInstance());
        tv.setText(spanString);
        tv.append(content);
    }
}
 
重写 LinkMovementMethod.onTouchEvent() 方法,当点击非富文本区域时,让TextView的父容器去执行点击事件; 
public class MyLinkedMovementMethod extends LinkMovementMethod {
        private static MyLinkedMovementMethod sInstance;
        public static MyLinkedMovementMethod getInstance() {
            if (sInstance == null)
                sInstance = new MyLinkedMovementMethod();
            return sInstance;
        }

        @Override
        public boolean onTouchEvent(TextView widget, Spannable buffer, MotionEvent event) {
            // 因为TextView没有点击事件,所以点击TextView的非富文本时,super.onTouchEvent()返回false;
            // 此时可以让TextView的父容器执行点击事件;
            boolean isConsume =  super.onTouchEvent(widget, buffer, event);
            if (!isConsume && event.getAction() == MotionEvent.ACTION_UP) {
                ViewParent parent = widget.getParent();
                if (parent instanceof ViewGroup) {
                    // 获取被点击控件的父容器,让父容器执行点击;
                    ((ViewGroup) parent).performClick();
                }
            }
            return isConsume;
        }
    }
 
 
四、参考 
android ClickableSpan intercepts the click event android - ListView: TextView with LinkMovementMethod makes list item unclickable? TextView ClickableSpan 事件分发的两个坑TextView ClickableSpan onClickListener点击事件冲突
 

以上是关于ClickableSpan事件和View.onClick()事件冲突的主要内容,如果未能解决你的问题,请参考以下文章

ClickableSpan空白区域也能点击的问题

ClickableSpan空白区域也能点击的问题

如何在 TextView 中获取 ClickableSpan 的坐标?

自定义 Spannable = ImageSpan + BackgroundColorSpan + ClickableSpan

如何改变ClickableSpan中下划线的颜色和样式

如何改变ClickableSpan中下划线的颜色和样式