自定义TextView解决Span点击&长按事件冲突

自定义TextView
import android.content.Context;
import android.text.Layout;
import android.text.Selection;
import android.text.Spannable;
import android.text.SpannableStringBuilder;
import android.text.TextPaint;
import android.text.method.BaseMovementMethod;
import android.text.style.ClickableSpan;
import android.text.style.URLSpan;
import android.util.AttributeSet;
import android.util.Log;
import android.view.MotionEvent;
import android.view.View;
import android.widget.TextView;

import androidx.annotation.NonNull;

/**
 * 文件名:TextViewFixTouchConsume
 * 描  述:
 * 作  者:
 * 时  间:2021/3/31 11:13
 */
public class TextViewFixTouchConsume extends TextView {

    public TextViewFixTouchConsume(Context context) {
        super(context);
        init();
    }

    public TextViewFixTouchConsume(Context context, AttributeSet attrs) {
        super(context, attrs);
        init();
    }

    public TextViewFixTouchConsume(
            Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
        init();
    }

    private void init() {
        setMovementMethod(ClickableMovementMethod.getInstance());
        setFocusable(false);
        setClickable(false);
        setLongClickable(false);
        try {
            final Spannable spannable = (Spannable) getText();
            final URLSpan[] urlSpans = spannable.getSpans(0, getText().length(), URLSpan.class);
            if (urlSpans != null && urlSpans.length > 0) {
                final SpannableStringBuilder spannableStringBuilder = new SpannableStringBuilder(spannable);
                for (URLSpan urlSpan : urlSpans) {
                    final String url = urlSpan.getURL();
                    spannableStringBuilder.setSpan(
//                            new URLSpan(url) {
                            new ClickableSpan() {
                                @Override
                                public void onClick(@NonNull View widget) {
                                    onLongClickListener.onClick(url);
                                }

                                @Override
                                public void updateDrawState(@NonNull TextPaint ds) {
                                    super.updateDrawState(ds);
                                    ds.setUnderlineText(false);
                                }
                            }, spannable.getSpanStart(urlSpan), spannable.getSpanEnd(urlSpan),
                            Spannable.SPAN_INCLUSIVE_EXCLUSIVE);
                }
                setText(spannableStringBuilder, TextView.BufferType.SPANNABLE);
            }
        } catch (Exception e) {
        }
    }

    long downTime;
    long moveTime;
    long upTime;

    public interface LongClickListener {
        void longClick();

        void onClick(String url);
    }

    LongClickListener onLongClickListener;

    public void setOnLongClickListener(LongClickListener onLongClickListener) {
        this.onLongClickListener = onLongClickListener;
    }

    boolean canLongClick;

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        if (event.getAction() == MotionEvent.ACTION_DOWN) {
            canLongClick = true;
            downTime = System.currentTimeMillis();
        } else if (event.getAction() == MotionEvent.ACTION_MOVE) {
            moveTime = System.currentTimeMillis();
            if (moveTime - downTime > 500 && canLongClick) {
                canLongClick = false;
                if (onLongClickListener != null)
                    onLongClickListener.longClick();
                return false;
            }
        } else if (event.getAction() == MotionEvent.ACTION_UP) {
            upTime = System.currentTimeMillis();
            if (upTime - downTime < 250) {
                if (onLongClickListener != null) {
                    onLongClickListener.onClick("empty url");
                }
            }
        }
        return super.onTouchEvent(event);
    }

    public static class ClickableMovementMethod extends BaseMovementMethod {

        private static ClickableMovementMethod sInstance;

        public static ClickableMovementMethod getInstance() {
            if (sInstance == null) {
                sInstance = new ClickableMovementMethod();
            }
            return sInstance;
        }

        @Override
        public boolean onTouchEvent(TextView widget, Spannable buffer, MotionEvent event) {

            int action = event.getActionMasked();
            if (action == MotionEvent.ACTION_UP || action == MotionEvent.ACTION_DOWN) {

                int x = (int) event.getX();
                int y = (int) event.getY();
                x -= widget.getTotalPaddingLeft();
                y -= widget.getTotalPaddingTop();
                x += widget.getScrollX();
                y += widget.getScrollY();
                Layout layout = widget.getLayout();
                int line = layout.getLineForVertical(y);
                int off = layout.getOffsetForHorizontal(line, x);

                ClickableSpan[] link = buffer.getSpans(off, off, ClickableSpan.class);
                if (link.length > 0) {
                    if (action == MotionEvent.ACTION_UP) {
                        link[0].onClick(widget);
                    } else {
                        Selection.setSelection(buffer, buffer.getSpanStart(link[0]),
                                buffer.getSpanEnd(link[0]));
                    }
                    return true;
                } else {
                    Selection.removeSelection(buffer);
                }
            }
            return false;
        }

        @Override
        public void initialize(TextView widget, Spannable text) {
            Selection.removeSelection(text);
        }
    }
}

xml布局
  <?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity">

    <FrameLayout
        android:id="@+id/container"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:background="#00FF00"
        android:padding="20dp"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintTop_toTopOf="parent">

        <com.xxx.xxx.TextViewFixTouchConsume
            android:id="@+id/textViewFixTouchConsume"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:autoLink="web"
            android:background="#FFFF00"
            android:text="测试测试测https://blog.csdn.net/qq_20330595试测试测试测试测试测试测试https://blog.csdn.net/qq_20330595测试测试测试测试测试" />

    </FrameLayout>
</androidx.constraintlayout.widget.ConstraintLayout>

使用

		textViewFixTouchConsume = findViewById(R.id.textViewFixTouchConsume);
        container = findViewById(R.id.container);

        container.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
               Log.d("xxx", "container Click");
            }
        });
        container.setOnLongClickListener(new View.OnLongClickListener() {
            @Override
            public boolean onLongClick(View view) {
               Log.d("xxx", "container longClick");
                //自己消费掉  不给点击事件
                return true;
            }
        });
        textViewFixTouchConsume.setOnLongClickListener(new TextViewFixTouchConsume.LongClickListener() {
            @Override
            public void longClick() {
                Log.d("xxx", "longClick");
            }

            @Override
            public void onClick(String url) {
                Log.d("xxx", url);
            }
        });
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值