您的问题的第一个答案是,您没有在您的TextView上设置一个点击监听器,该消息是以user2558882指出的方式消耗点击事件.在您的TextView上设置了一个点击监听器后,您将看到ClickableSpans触摸区域之外的区域将按预期工作.但是,您会发现当您点击其中一个ClickableSpans时,TextView的onClick回调也将被触发.如果两个火都是你的一个问题,那会导致我们遇到一个困难的问题. user2558882的回复不能保证您的ClickableSpan的onClick回调将在您的TextView之前被触发.这里有一些
a similar thread的解决方案可以更好地实现,并从源代码中解释.线程应该适用于大多数设备的接受答案,但该答案的评论提到某些设备有问题.看起来像一些具有自定义操作符/制造商用户界面的设备是责怪的,但这是猜测.
那么为什么你不能保证onClick回调顺序?如果你看看TextView(Android 4.3)的源码,你会注意到在onTouchEvent方法中,boolean superResult = super.onTouchEvent(event); (super is View)被调用before | = mMovement.onTouchEvent(this,(Spannable)mText,event);这是调用您的移动方法,然后调用您的ClickableSpan的onClick.看看超级(View)onTouchEvent(..),你会注意到:
// Use a Runnable and post this rather than
// performClick directly. This lets other visual
// of the view update before click actions start.
if (mPerformClick == null) {
mPerformClick = new PerformClick();
}
if (!post(mPerformClick)) { //
performClick(); // it'll fallback to calling it directly
}
performClick()调用click listener集,在这种情况下,它是我们的TextView的点击监听器.这意味着,你不会知道你的onClick回调将以什么顺序触发.你所知道的是,您的ClickableSpan和TextView点击监听器将被调用.我之前提到的线程的解决方案有助于确保顺序,以便您可以使用标志.
如果确保与许多设备的兼容性是优先考虑的,那么最好先看看您的布局,看看是否可以避免被困在这种情况.通常有很多布局选项来像这样的裙子.
编辑评论答案:
当您的TextView执行onTouchEvent时,它会调用您的LinkMovementMethod的onTouchEvent,以便它可以处理对各种ClickableSpan的onClick方法的调用.您的LinkMovementMethod在其onTouchEvent中执行以下操作:
@Override
public boolean onTouchEvent(TextView widget, Spannable buffer,
MotionEvent event) {
int action = event.getAction();
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 if (action == MotionEvent.ACTION_DOWN) {
Selection.setSelection(buffer,
buffer.getSpanStart(link[0]),
buffer.getSpanEnd(link[0]));
}
return true;
} else {
Selection.removeSelection(buffer);
}
}
return super.onTouchEvent(widget, buffer, event);
}
您会注意到,运动MotionEvent将获取动作(ACTION_UP:提升手指,ACTION_DOWN:按下手指),触发起始位置的x和y坐标,然后找到哪个行号和偏移量(文本中的位置)触摸命中.最后,如果有ClickableSpans包含这一点,它们将被检索,并且它们的onClick方法被调用.因为我们想要传递任何触摸到你的父布局,你可以调用你的布局onTouchEvent,如果你想要做它做的一切,当触摸,或者你可以称之为点击监听器,如果它实现了你所需的功能.这里是哪里:
if (link.length != 0) {
if (action == MotionEvent.ACTION_UP) {
link[0].onClick(widget);
} else if (action == MotionEvent.ACTION_DOWN) {
Selection.setSelection(buffer,
buffer.getSpanStart(link[0]),
buffer.getSpanEnd(link[0]));
}
return true;
} else {
Selection.removeSelection(buffer);
// Your call to your layout's onTouchEvent or it's
//onClick listener depending on your needs
}
}
所以要查看,您将创建一个扩展LinkMovementMethod的新类,覆盖它的onTouchEvent方法,将该源复制并粘贴到正确的位置,并将其粘贴到我所评论的正确位置,确保将TextView的移动方法设置为新的子类,你应该设置
再次编辑可能的副作用避免看看ScrollingMovementMethod的源(LinkMovementMethod的父),你会看到它是一个委托方法,它调用一个静态方法返回Touch.onTouchEvent(widget,buffer,event);这意味着您可以将其添加为方法中的最后一行,并避免调用super(LinkMovementMethod)onTouchEvent实现,这将重复您要粘贴的内容,其他事件可能按预期方式崩溃.