Android 使用ClickableSpan将TextView上的触摸事件重定向到父视图

Android 使用ClickableSpan将TextView上的触摸事件重定向到父视图,android,touch,linkmovementmethod,Android,Touch,Linkmovementmethod,我有一个带有文本视图的RecyclerView,可以包含自定义的标签,这些标签应该是可点击的。所以我创建了TextView的子类,在其中使用模式创建ClickableSpan。为了激活ClickableSpan,我添加了 setMovementMethod(LinkMovementMethod.getInstance()) 此方法更改TextView的属性: setFocusable(true); setClickable(true); setLongClickable(true); 单击链

我有一个带有文本视图的RecyclerView,可以包含自定义的标签,这些标签应该是可点击的。所以我创建了TextView的子类,在其中使用模式创建ClickableSpan。为了激活ClickableSpan,我添加了

setMovementMethod(LinkMovementMethod.getInstance())
此方法更改TextView的属性:

setFocusable(true);
setClickable(true);
setLongClickable(true);
单击链接可以工作,但它阻止ripple drawable显示在列表项上,并且忽略hashtags之外的单击TextView


所以我很感兴趣的是,如何将TextView上的触摸重定向到它的父对象上(除了hashtag之外?

所以我还没有在internet上找到解决方案,所以我最终自己实现了它

因此,我没有使用简单的TextView和MovementMethod,而是使用onTouchEvent创建了一个扩展文本视图的新类

public class MyTextView extends TextView {
@Override
  public boolean onTouchEvent(@NonNull MotionEvent event) {
    if (linksActive) {
      Layout layout = this.getLayout();
      if (layout != null) {
        int line = layout.getLineForVertical((int) event.getY());
        int offset = layout.getOffsetForHorizontal(line, event.getX());

        if (getText() != null && getText() instanceof Spanned) {
          Spanned spanned = (Spanned) getText();

          ClickableSpan[] links = spanned.getSpans(offset, offset, ClickableSpan.class);

          if (links.length > 0) {

            if (event.getAction() == MotionEvent.ACTION_DOWN) {
              return true;
            } else if (event.getAction() == MotionEvent.ACTION_UP) {
              links[0].onClick(this);
            } else {
              return super.onTouchEvent(event);
            }
          }
        }
      }
    }

    return super.onTouchEvent(event);
  }
}
我还创建了一个扩展ClickableSpan的类,该类处理点击:

public class URLSpan extends ClickableSpan {
  private String url;
  private int spanType;

  public URLSpan(String url, int spanType) {
    this.url = url;
    this.spanType = spanType;
  }

  public int getType() {
    return spanType;
  }

  @Override
  public void onClick(@NonNull View widget) {
    if (widget instanceof OnSpanClickListener) {
      ((OnSpanClickListener) widget).onSpanClicked(url, type);
    } else {
      // ignored
    }
  }

}
在这种情况下,我必须自己管理跨度(在
setText()
method中)

多亏了维克多--

我使用它来允许不在可点击范围内的触摸事件传递给家长

override fun dispatchTouchEvent(event: MotionEvent?): Boolean {

        if (hasOnClickListeners() == false && linksClickable == false) {
            // Avoid all touches
            return false
        }

        // Handle avoiding touch events, if not on a clickable span
        if (event != null && hasOnClickListeners() == false) {
            if (layout != null) {
                val line = layout.getLineForVertical(event.y.toInt())
                val offset = layout.getOffsetForHorizontal(line, event.x)

                if (text != null && text is Spanned) {
                    val spanned = text as Spanned

                    val spansAtPoint = spanned.getSpans(offset, offset, ClickableSpan::class.java)
                    if (spansAtPoint.size == 0) {
                        // No clickable span at event -- abort touch
                        return false
                    }
                }
            }
        }
        return super.dispatchTouchEvent(event)
    }

我也有同样的问题,这个解决方案已经起作用了吗?能否将onClick传递给父级(以及连锁反应)而不是TextView?何时设置linksActive?我将该属性添加为xml属性,并为该类添加了公共setter
override fun dispatchTouchEvent(event: MotionEvent?): Boolean {

        if (hasOnClickListeners() == false && linksClickable == false) {
            // Avoid all touches
            return false
        }

        // Handle avoiding touch events, if not on a clickable span
        if (event != null && hasOnClickListeners() == false) {
            if (layout != null) {
                val line = layout.getLineForVertical(event.y.toInt())
                val offset = layout.getOffsetForHorizontal(line, event.x)

                if (text != null && text is Spanned) {
                    val spanned = text as Spanned

                    val spansAtPoint = spanned.getSpans(offset, offset, ClickableSpan::class.java)
                    if (spansAtPoint.size == 0) {
                        // No clickable span at event -- abort touch
                        return false
                    }
                }
            }
        }
        return super.dispatchTouchEvent(event)
    }