问题 — 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

由上表可知:

  1. 添加富文本点击事件 和 TextView点击事件时,点击富文本时,会触发 富文本的点击事件TextView的点击事件
  2. 添加富文本点击事件 和 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;
        }
    }

四、参考

  1. android ClickableSpan intercepts the click event
  2. android - ListView: TextView with LinkMovementMethod makes list item unclickable?
  3. TextView ClickableSpan 事件分发的两个坑
  4. TextView ClickableSpan onClickListener点击事件冲突
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值