Android 設定了ClickableSpan導致的上層View點選事件無法響應解決方案
阿新 • • 發佈:2019-01-22
首先感謝此題主及回答的大神提供了思路:點選開啟連結
首先問題是這樣的:如果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方法,否則不攔截。
效果如下: