4

我有一个显示一些 HTML 代码(包括图像,ImageGetter)的 TextView。html 不是我的,但我可以要求他们包含自定义方案链接,甚至可能是标签。

目的:显示一些动态生成的内容,而无需使用嵌套的 Android 布局。
问题:必须在应用程序中处理某些链接(加载了新的 Fragment)。
不能为 action.VIEW 使用接收器,因为它是一个 Activity Intent,而不是广播,并且它的使用将是非常上下文相关的,因此只有以编程方式注册的接收器才可以。

我正在使用textView.setText(Html.fromHtml(content.content, imageGetter, null). 需要一切都保持不变,除了一些跨度应该有我自己onClick的。我对 Spanned 不是很熟悉,所以我看到了这些选项:

  1. 编辑从返回的 SpannableStringBuilder Html.fromHtml,并用自定义 ClickableSpan 替换我想要的 URLSpan(如何?)
  2. 如上所述,但将所有内容复制到新的构建器,将 URLSpan 交换为我自己的(如何?append只需要一个 CharSequence,我得到 RelativeSizeSpan、StyleSpan、ImageSpan、URLSpan ......)
  3. 手动创建一个 Spaned。我可以为自定义方案做到这一点,但是如何复制Html.fromHtml(或足够接近)其他所有的效果?

[编辑] 感谢 MH。信息。我以前尝试过,但失败了。现在我回到它,我发现我犯了一个错误,将错误的项目传递给 setSpan 的第一个参数。
如果有人感兴趣,我现在使用这个:

public static interface OnSpanClickListener {
    void onClick(String url);
}
public static SpannableStringBuilder getSpannable(String source, ImageGetter imageGetter, String scheme, final OnSpanClickListener clickHandler){
    SpannableStringBuilder b = (SpannableStringBuilder) Html.fromHtml(source, imageGetter, null);
    for(URLSpan s : b.getSpans(0, b.length(), URLSpan.class)){
        String s_url = s.getURL();
        if(s_url.startsWith(scheme+"://")){
            URLSpan newSpan = new URLSpan(s_url.substring(scheme.length()+3)){
                public void onClick(View view) {
                    clickHandler.onClick(getURL());
                }
            };
            b.setSpan(newSpan, b.getSpanStart(s), b.getSpanEnd(s), b.getSpanFlags(s));
            b.removeSpan(s);
        }
    }
    return b;
}

(...)

body.setText(getSpannable(content.content, imageGetter, getResources().getString(R.string.poster_scheme), new OnSpanClickListener(){
public void onClick(String url) {
    // do whatever
}
}));
4

2 回答 2

3

选项 1 可能是最直接的,并且之前已经完成了大部分艰苦的工作。您的总体思路是正确的:处理 HTML 后,您可以请求所有生成的URLSpan实例并循环遍历它们。然后,您可以将其替换为自定义的可点击跨度,以完全控制任何跨度点击。

在下面的示例中,我只是将 every 替换URLSpan为该类的一个简单扩展,该类采用原始 url(实际上,我可能应该说 'uri')并替换其方案部分。我没有onClick()实现实际的逻辑,但我会把它留给你的想象。

SpannableStringBuilder builder = ...
URLSpan[] spans = builder .getSpans(0, builder .length(), URLSpan.class);
for (URLSpan span : spans) {
    int start = builder .getSpanStart(span);
    int end = builder .getSpanEnd(span);
    s.removeSpan(span);
    span = new CustomURLSpan(span.getURL().replace("http://", "scheme://"));
    s.setSpan(span, start, end, 0);
}
textView.setText(builder);

如前所述,这里的CustomURLSpan类是一个简单的扩展,URLSpan它接受一个 url 并覆盖onClick()方法,因此我们自己的逻辑可以在那里执行。

public class CustomURLSpan extends URLSpan {

    public CustomURLSpan(String url) {
        super(url);
    }

    @Override public void onClick(View widget) {
        // custom on click behaviour here
    }
}

一些基本做类似事情的相关问答(可能有助于获得更多灵感):

于 2013-04-11T09:52:06.243 回答
1

由于您将使用TextView.setMovementMethod(LinkMovementMethod.getInstance())使其响应部分点击,您可以实现自定义的LinkMovementMethod来拦截点击事件并处理您自己的逻辑。

public class CustomLinkMovementMethod extends LinkMovementMethod {

    private HashMap<Class, SpanProxy> spansProxy = new HashMap<>();

    public CustomLinkMovementMethod setSpanProxy(Class spanClazz, SpanProxy proxy) {
        spansProxy.put(spanClazz, proxy);
        return this;
    }

    public CustomLinkMovementMethod removeSpanProxy(Class spanClazz) {
        spansProxy.remove(spanClazz);
        return this;
    }

    @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) {
                    SpanProxy spanProxy = spansProxy.get(ClickableSpan.class);
                    if(spanProxy != null) {
                        spanProxy.proxySpan(link[0], widget);
                    } else {
                        link[0].onClick(widget);
                    }
                    return true;
                }
            }
        }
            return super.onTouchEvent(widget, buffer, event);
    }


    public interface SpanProxy {
        void proxySpan(Object span, View widget);
    }
}

并像这样使用:

textView.setMovementMethod(new CustomLinkMovementMethod().setSpanProxy(ClickableSpan.class,
        new CustomLinkMovementMethod.SpanProxy() {
            @Override
            public void proxySpan(Object span, View widget) {
                if (span instanceof URLSpan) {
                    URLSpan urlSpan = (URLSpan) span;
                    String url = urlSpan.getURL();

                    // do what you want with the link
                    // tbd
                    return;
                }

                if (span instanceof ClickableSpan) {
                    ((ClickableSpan) span).onClick(widget);
                }
            }
        }));
于 2017-02-14T10:00:08.740 回答