1. 程式人生 > >Android 設定了ClickableSpan導致的上層View點選事件無法響應解決方案

Android 設定了ClickableSpan導致的上層View點選事件無法響應解決方案

首先感謝此題主及回答的大神提供了思路:點選開啟連結

  首先問題是這樣的:如果LinearLayout中包了一個TextView,TextView設定了ClickableSpan,這時如果恰巧LinearLayout和TextView都設定了點選監聽,這時如果點選了ClickableSpan以外的文字,LinearLayout是接收不到監聽事件的。

比如:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical">

    <LinearLayout
        android:id="@+id/ll_sp"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:orientation="vertical"
        android:paddingTop="30dp">

        <TextView
            android:id="@+id/tv_sp"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content" />
    </LinearLayout>

</LinearLayout>
package com.muzi.spannablestring;

import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.text.SpannableString;
import android.text.SpannableStringBuilder;
import android.text.Spanned;
import android.text.method.LinkMovementMethod;
import android.text.style.ClickableSpan;
import android.view.View;
import android.widget.LinearLayout;
import android.widget.TextView;
import android.widget.Toast;

public class MainActivity extends AppCompatActivity implements View.OnClickListener{

    private LinearLayout mSpLayout;
    private TextView mTvSp;

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

        mSpLayout = (LinearLayout) findViewById(R.id.ll_sp);
        mTvSp = (TextView) findViewById(R.id.tv_sp);

        mSpLayout.setOnClickListener(this);
        mMySpLayout.setOnClickListener(this);

        SpannableStringBuilder ssb = new SpannableStringBuilder("nishisdfhisafhisafaof   ");
        SpannableString spannableString = new SpannableString("btn");
        spannableString.setSpan(new ClickableSpan() {
            @Override
            public void onClick(View widget) {
                Toast.makeText(getApplicationContext(), "click btn", Toast.LENGTH_SHORT).show();
            }
        }, 0, 3, Spanned.SPAN_INCLUSIVE_EXCLUSIVE);
        ssb.append(spannableString);
        mTvSp.setText(ssb);
        mTvSp.setMovementMethod(new LinkMovementMethod());
    }

    @Override
    public void onClick(View view) {
        switch (view.getId()) {
            case R.id.ll_sp:
                Toast.makeText(getApplicationContext(), "click ll", Toast.LENGTH_SHORT).show();
                break;
            default:
                break;
        }
    }
}

效果如下:

   

解決方案:

package com.muzi.spannablestring;

import android.text.Layout;
import android.text.Selection;
import android.text.Spannable;
import android.text.method.LinkMovementMethod;
import android.text.style.ClickableSpan;
import android.view.MotionEvent;
import android.widget.TextView;


public class LinkTouchMovementMethod extends LinkMovementMethod {

    private ClickableSpan mPressedSpan;

    @Override
    public boolean onTouchEvent(TextView textView, Spannable spannable, MotionEvent event) {

        if (event.getAction() == MotionEvent.ACTION_DOWN) {
            mPressedSpan = getPressedSpan(textView, spannable, event);
            if (mPressedSpan != null) {
                Selection.setSelection(spannable, spannable.getSpanStart(mPressedSpan),
                        spannable.getSpanEnd(mPressedSpan));
            }
        } else if (event.getAction() == MotionEvent.ACTION_MOVE) {
            ClickableSpan touchedSpan = getPressedSpan(textView, spannable, event);
            if (mPressedSpan != null && touchedSpan != mPressedSpan) {
                mPressedSpan = null;
                Selection.removeSelection(spannable);
            }
        } else {
            if (mPressedSpan != null) {
                super.onTouchEvent(textView, spannable, event);
            }
            mPressedSpan = null;
            Selection.removeSelection(spannable);
        }
        return mPressedSpan != null;
    }

    /**
     * Copy from:
     *  http://stackoverflow.com/questions/20856105/change-the-text-color-of-a-single-clickablespan-when-pressed-without-affecting-o
     * By:
     *  Steven Meliopoulos
     */
    private ClickableSpan getPressedSpan(TextView textView, Spannable spannable, MotionEvent event) {

        int x = (int) event.getX();
        int y = (int) event.getY();

        x -= textView.getTotalPaddingLeft();
        y -= textView.getTotalPaddingTop();

        x += textView.getScrollX();
        y += textView.getScrollY();

        Layout layout = textView.getLayout();
        int line = layout.getLineForVertical(y);
        int off = layout.getOffsetForHorizontal(line, x);

        ClickableSpan[] link = spannable.getSpans(off, off, ClickableSpan.class);
        ClickableSpan touchedSpan = null;
        if (link.length > 0) {
            touchedSpan = link[0];
        }
        return touchedSpan;
    }

    public boolean isPressedSpan() {
        return mPressedSpan != null;
    }
}
package com.muzi.spannablestring;

import android.content.Context;
import android.support.annotation.Nullable;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.widget.TextView;

/**
 * Created by linpu on 18-3-28.
 */

public class MySpannableTextView extends TextView {

    private LinkTouchMovementMethod mLinkTouchMovementMethod;

    public MySpannableTextView(Context context) {
        super(context);
    }

    public MySpannableTextView(Context context, @Nullable AttributeSet attrs) {
        super(context, attrs);
    }

    public MySpannableTextView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
    }

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        boolean result = super.onTouchEvent(event);
        return mLinkTouchMovementMethod != null ? mLinkTouchMovementMethod.isPressedSpan() : result;
    }

    public void setLinkTouchMovementMethod(LinkTouchMovementMethod linkTouchMovementMethod) {
        mLinkTouchMovementMethod = linkTouchMovementMethod;
    }
}

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical">

    <LinearLayout
        android:id="@+id/ll_sp"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:orientation="vertical"
        android:paddingTop="30dp">

        <TextView
            android:id="@+id/tv_sp"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content" />
    </LinearLayout>

    <LinearLayout
        android:id="@+id/my_ll_sp"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:orientation="vertical"
        android:paddingTop="30dp"
        android:visibility="gone">

        <com.muzi.spannablestring.MySpannableTextView
            android:id="@+id/my_tv_sp"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content" />
    </LinearLayout>

</LinearLayout>

package com.muzi.spannablestring;

import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.text.SpannableString;
import android.text.SpannableStringBuilder;
import android.text.Spanned;
import android.text.method.LinkMovementMethod;
import android.text.style.ClickableSpan;
import android.view.View;
import android.widget.LinearLayout;
import android.widget.TextView;
import android.widget.Toast;

public class MainActivity extends AppCompatActivity implements View.OnClickListener{

    private LinearLayout mSpLayout;
    private TextView mTvSp;

    private LinearLayout mMySpLayout;
    private MySpannableTextView mMyTvSp;

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

        mSpLayout = (LinearLayout) findViewById(R.id.ll_sp);
        mTvSp = (TextView) findViewById(R.id.tv_sp);

        mMySpLayout = (LinearLayout) findViewById(R.id.my_ll_sp);
        mMyTvSp = (MySpannableTextView) findViewById(R.id.my_tv_sp);

        mSpLayout.setOnClickListener(this);
        mMySpLayout.setOnClickListener(this);

        SpannableStringBuilder ssb = new SpannableStringBuilder("nishisdfhisafhisafaof   ");
        SpannableString spannableString = new SpannableString("btn");
        spannableString.setSpan(new ClickableSpan() {
            @Override
            public void onClick(View widget) {
                Toast.makeText(getApplicationContext(), "click btn", Toast.LENGTH_SHORT).show();
            }
        }, 0, 3, Spanned.SPAN_INCLUSIVE_EXCLUSIVE);
        ssb.append(spannableString);
        mTvSp.setText(ssb);
        mTvSp.setMovementMethod(new LinkMovementMethod());

        SpannableStringBuilder ssb2 = new SpannableStringBuilder("nishisdfhisafhisafaof   ");
        SpannableString spannableString2 = new SpannableString("mybtn");
        spannableString2.setSpan(new ClickableSpan() {
            @Override
            public void onClick(View view) {
                Toast.makeText(getApplicationContext(), "click my btn", Toast.LENGTH_SHORT).show();
            }
        }, 0, 5, Spanned.SPAN_INCLUSIVE_EXCLUSIVE);
        ssb2.append(spannableString2);
        mMyTvSp.setText(ssb2);
        LinkTouchMovementMethod linkTouchMovementMethod = new LinkTouchMovementMethod();
        mMyTvSp.setLinkTouchMovementMethod(linkTouchMovementMethod);
        mMyTvSp.setMovementMethod(linkTouchMovementMethod);
    }

    @Override
    public void onClick(View view) {
        switch (view.getId()) {
            case R.id.ll_sp:
                Toast.makeText(getApplicationContext(), "click ll", Toast.LENGTH_SHORT).show();
                break;
            case R.id.my_ll_sp:
                Toast.makeText(getApplicationContext(), "click my ll", Toast.LENGTH_SHORT).show();
                break;
            default:
                break;
        }
    }
}

解決的思路就是:點選時,判斷當前點選的區域是否有ClickableSpan,如果有,則攔截TextView的onTouchEvent方法,否則不攔截。

效果如下: