Android:可点击 TextView 中的 ClickableSpan
我有一个可以包含可点击链接的文本视图.单击其中一个链接时,我想开始一项活动.这很好用,但也应该可以单击整个文本视图并开始另一个活动.
I have a textview that can contain clickable links. When one of this links is clicked, I want to start an activity. This works fine, but it should also be possible to click the whole textview and start another activity.
这就是我目前的解决方案:
So that's my current solution:
TextView tv = (TextView)findViewById(R.id.textview01);
Spannable span = Spannable.Factory.getInstance().newSpannable("test link span");
span.setSpan(new ClickableSpan() {
@Override
public void onClick(View v) {
Log.d("main", "link clicked");
Toast.makeText(Main.this, "link clicked", Toast.LENGTH_SHORT).show();
} }, 5, 9, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
tv.setText(span);
tv.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
Log.d("main", "textview clicked");
Toast.makeText(Main.this, "textview clicked", Toast.LENGTH_SHORT).show();
}
});
tv.setMovementMethod(LinkMovementMethod.getInstance());
问题是,当我设置 OnClickListener 时,每次点击链接时首先整个文本视图的监听器然后调用 ClickableSpan 的监听器.
The problem is, that when I set an OnClickListener, everytime I click on a link first the listener for the whole textview and then the one for the ClickableSpan is called.
当点击链接时,有没有办法阻止 android 调用整个 textview 的侦听器?或者在整个视图的监听器中决定是否点击了链接?
Is there a way to prevent android from calling the listener for the whole textview, when a link is clicked? Or to decide in the listener for the whole view, if a link was clicked or not?
推荐答案
Matthew 建议对 TextView 进行子类化,并提出了一个相当丑陋的解决方法.但它有效:
Matthew suggested subclassing TextView and with that hint a came up with a rather ugly workaround. But it works:
我创建了一个ClickPreventableTextView",当我在 TextView 中有可点击的跨度时,我会使用它作为一个整体应该是可点击的.
I've created a "ClickPreventableTextView" which I use when I have clickablespans in a TextView that should be clickable as a whole.
在其 onTouchEvent 方法中,该类在调用其基 TextView 类的 onTouchEvent 之前调用 MovementMethod 的 onTouchEvent 方法.因此,可以保证 clickablespan 的 Listener 将首先被调用.而且我可以防止为整个 TextView 调用 OnClickListener
In its onTouchEvent method this class calls the onTouchEvent method of MovementMethod before calling onTouchEvent on its base TextView class. So it is guaranted, that the Listener of the clickablespan will be invoked first. And I can prevent invoking the OnClickListener for the whole TextView
/**
* TextView that allows to insert clickablespans while whole textview is still clickable<br>
* If a click an a clickablespan occurs, click handler of whole textview will <b>not</b> be invoked
* In your span onclick handler you first have to check whether {@link ignoreSpannableClick} returns true, if so just return from click handler
* otherwise call {@link preventNextClick} and handle the click event
* @author Lukas
*
*/
public class ClickPreventableTextView extends TextView implements OnClickListener {
private boolean preventClick;
private OnClickListener clickListener;
private boolean ignoreSpannableClick;
public ClickPreventableTextView(Context context) {
super(context);
}
public ClickPreventableTextView(Context context, AttributeSet attrs) {
super(context, attrs);
}
public ClickPreventableTextView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
}
public boolean onTouchEvent(MotionEvent event) {
if (getMovementMethod() != null)
getMovementMethod().onTouchEvent(this, (Spannable)getText(), event);
this.ignoreSpannableClick = true;
boolean ret = super.onTouchEvent(event);
this.ignoreSpannableClick = false;
return ret;
}
/**
* Returns true if click event for a clickable span should be ignored
* @return true if click event should be ignored
*/
public boolean ignoreSpannableClick() {
return ignoreSpannableClick;
}
/**
* Call after handling click event for clickable span
*/
public void preventNextClick() {
preventClick = true;
}
@Override
public void setOnClickListener(OnClickListener listener) {
this.clickListener = listener;
super.setOnClickListener(this);
}
@Override
public void onClick(View v) {
if (preventClick) {
preventClick = false;
} else if (clickListener != null)
clickListener.onClick(v);
}
}
可点击范围的监听器现在看起来像这样
The listener for the clickable span now looks like that
span.setSpan(new ClickableSpan() {
@Override
public void onClick(View v) {
Log.d("main", "link clicked");
if (widget instanceof ClickPreventableTextView) {
if (((ClickPreventableTextView)widget).ignoreSpannableClick())
return;
((ClickPreventableTextView)widget).preventNextClick();
}
Toast.makeText(Main.this, "link clicked", Toast.LENGTH_SHORT).show();
} }, 5, 9, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
对我来说主要的缺点是,现在 getMovementMethod().onTouchEvent 将被调用两次(TextView 在它的 onTouchEvent 方法中调用该方法).我不知道这是否有任何副作用,atm 它按预期工作.
For me the main disadvantage is, that now getMovementMethod().onTouchEvent will be called twice (TextView calls that method in it's onTouchEvent method). I don't know if this has any side effects, atm it works as expected.
相关文章