【问题标题】:Redirect touch event on TextView with ClickableSpan to parent view使用 ClickableSpan 将 TextView 上的触摸事件重定向到父视图
【发布时间】:2015-05-26 07:50:41
【问题描述】:

我有一个带有 TextViews 的 RecyclerView,它可能包含应该可以点击的自定义主题标签。所以我创建了 TextView 的子类,在其中使用 Pattern 我创建了 ClickableSpan。为了使 ClickableSpan 处于活动状态,我添加了

setMovementMethod(LinkMovementMethod.getInstance())

这个方法改变了TextView的属性:

setFocusable(true);
setClickable(true);
setLongClickable(true);

点击链接有效,但它会阻止可绘制的波纹显示在列表项上,并且点击标签之外的 TextView 将被忽略。

所以我很感兴趣如何将 TextView 上的触摸(除主题标签之外的所有内容)重定向到它的父级?

【问题讨论】:

    标签: android touch linkmovementmethod


    【解决方案1】:

    感谢维克多 --

    我使用它来允许可点击跨度上的触摸事件传递给父对象

    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)
        }
    

    【讨论】:

      【解决方案2】:

      所以我在互联网上没有找到解决方案,所以我最终自己实现了它。

      因此,我没有使用带有 MovementMethod 的简单 TextView,而是创建了一个使用 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() 方法中)

      public static void formatPhoneNumbers(@NonNull Spannable spannable, @ColorInt int color) {
          Matcher matcher = MY_Patterns.PHONE_NO.matcher(spannable);
          while (matcher.find()) {
            if (!Linkify.sPhoneNumberMatchFilter.acceptMatch(spannable, matcher.start(), matcher.end())) {
              continue;
            }
      
            removeSpans(spannable, SpanType.PHONE_NO, matcher.start(), matcher.end());
      
            if (hasClickableSpans(spannable, matcher.start(), matcher.end())) {
              // ignore different clickable spans overlap
              continue;
            }
      
            final ForegroundColorSpan span = new ForegroundColorSpan(color);
            final ClickableSpan urlSpan = new URLSpan(matcher.group(), SpanType.PHONE_NO);
      
            spannable.setSpan(urlSpan, matcher.start(), matcher.end(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
            spannable.setSpan(span, matcher.start(), matcher.end(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
          }
        }
      
        private static void removeSpans(@NonNull Spannable spannable, int type, int start, int end) {
          URLSpan[] arr = spannable.getSpans(start, end, URLSpan.class);
          for (URLSpan span : arr) {
            if (span.getType() == type) {
              spannable.removeSpan(span);
            }
          }
        }
      
        private static boolean hasClickableSpans(@NonNull Spannable spannable, int start, int end) {
          ClickableSpan[] arr = spannable.getSpans(start, end, ClickableSpan.class);
          return arr.length > 0;
        }
      

      【讨论】:

      • 我有同样的问题,这个解决方案已经有效了吗?您能否将 onClick 传递给父级(以及涟漪效应)而不是 TextView ?
      • 什么时候设置linksActive?
      • 我将该属性添加为 xml 属性,并为该类添加了公共设置器
      猜你喜欢
      • 2016-10-27
      • 1970-01-01
      • 2017-04-28
      • 2011-09-16
      • 2014-07-24
      • 1970-01-01
      • 1970-01-01
      • 2014-05-21
      • 1970-01-01
      相关资源
      最近更新 更多