在另一个活动中打开TextView链接,而不是默认浏览器。

18

autoLinkMask 设置为 Linkify.ALL 的 textView 具有打开链接并在浏览器中显示网页的功能。

我需要调用另一个活动,该活动将使用其webView而不离开应用程序来执行此操作。

重要提示:

  • 更改textView内容不是选项,我需要显示链接及其具有的方案,
  • textView中有很多文本,不仅是链接。

我查看了 movementMethodIntentFilters,可能漏掉了一些东西,但看起来它无法帮助我。

因此,在TextView中拦截所选链接以执行某些操作而不是打开浏览器的任何选项?

如果您想提到 这个 SO问题,请给出一些理由,因为它似乎不能解决我的问题。


1
你链接到的 SO 问题包含了一个正确答案的精髓:用自定义的 ClickableSpan 替换 Linkify 生成的 URLSpan 对象,以实现你所需的功能。 - CommonsWare
你想在文本点击时在另一个活动的 WebView 中打开链接吗? - AkashG
@AkashG 没错,我想获取被触摸的URL,并在另一个活动中使用它,在Web视图中显示。将保持应用程序风格的稳定性,我不想因为用户自己配置了自定义浏览器而受到责备。 - A-Live
@CommonsWare 是的,它可以工作,但弱点在于我必须手动查找所有链接并将它们标记为可点击的,而且解决方案必须改进以将点击的文本传递给 onClick 事件。不过我喜欢的是它也可以链接化标签!请把您的评论改写成答案,谢谢您。 - A-Live
5个回答

26

步骤1:创建自己的ClickableSpan子类,使其在onClick()方法中实现所需功能(例如,命名为YourCustomClickableSpan

步骤2:运行批量转换所有URLSpan对象为YourCustomClickableSpan对象。我有一个实用类可供使用:

public class RichTextUtils {
    public static <A extends CharacterStyle, B extends CharacterStyle> Spannable replaceAll(Spanned original,
    Class<A> sourceType,
    SpanConverter<A, B> converter) {
        SpannableString result=new SpannableString(original);
        A[] spans=result.getSpans(0, result.length(), sourceType);

        for (A span : spans) {
            int start=result.getSpanStart(span);
            int end=result.getSpanEnd(span);
            int flags=result.getSpanFlags(span);

            result.removeSpan(span);
            result.setSpan(converter.convert(span), start, end, flags);
        }

        return(result);
    }

    public interface SpanConverter<A extends CharacterStyle, B extends CharacterStyle> {
        B convert(A span);
    }
}

你可以像这样使用它:
yourTextView.setText(RichTextUtils.replaceAll((Spanned)yourTextView.getText(),
                                             URLSpan.class,
                                             new URLSpanConverter()));

使用自定义的URLSpanConverter,像这样:

class URLSpanConverter
      implements
      RichTextUtils.SpanConverter<URLSpan, YourCustomClickableSpan> {
    @Override
    public URLSpan convert(URLSpan span) {
      return(new YourCustomClickableSpan(span.getURL()));
    }
  }

将所有URLSpan对象转换为YourCustomClickableSpan对象。


那使得解决方案非常完整,再次感谢。我发现它比上面评论中讨论的另一个解决方案更好,事实上看起来非常酷。 - A-Live
@A-Live 我按照这个解决方案,但是TextView中的链接无法点击。我尝试切换android:linksClickable并尝试不同的android:autoLink值。你有什么想法吗? - Ravi
1
@Ravi,我不确定你的意思或者最好的帮助方式是什么,但是创建一个单独的问题并尽可能提供详细信息肯定是可行的。 - A-Live
这个解决方案不仅在常规使用中有效,而且在可访问性模式下的 TalkBack 中也有效 :) - Abhinav Tyagi

3

我通过添加点击监听器,使@CommonsWare的代码更加优雅和清晰,可以直接将其添加到替换URL Spans的同一方法中。

  1. Define YourCustomClickableSpan Class.

     public static class YourCustomClickableSpan extends ClickableSpan {
    
        private String url;
        private OnClickListener mListener;
    
        public YourCustomClickableSpan(String url, OnClickListener mListener) {
            this.url = url;
            this.mListener = mListener;
        }
    
        @Override
        public void onClick(View widget) {
            if (mListener != null) mListener.onClick(url);
        }
    
        public interface OnClickListener {
            void onClick(String url);
        }
    }
    
  2. Define RichTextUtils Class which will manipulate the Spanned text of your TextView.

    public static class RichTextUtils {
    
        public static <A extends CharacterStyle, B extends CharacterStyle> Spannable replaceAll (
              Spanned original,
              Class<A> sourceType,
              SpanConverter<A, B> converter,
              final ClickSpan.OnClickListener listener) {
    
                  SpannableString result = new SpannableString(original);
                  A[] spans = result.getSpans(0, result.length(), sourceType);
    
                  for (A span : spans) {
                      int start = result.getSpanStart(span);
                      int end = result.getSpanEnd(span);
                      int flags = result.getSpanFlags(span);
    
                      result.removeSpan(span);
                      result.setSpan(converter.convert(span, listener), start, end, flags);
                  }
    
                  return (result);
              }
    
              public interface SpanConverter<A extends CharacterStyle, B extends CharacterStyle> {
                  B convert(A span, ClickSpan.OnClickListener listener);
    
         }
    }
    
  3. Define URLSpanConverter class which will do the actual code of replacement URLSpan with Your Custom Span.

        public static class URLSpanConverter
            implements RichTextUtils.SpanConverter<URLSpan, ClickSpan> {
    
    
            @Override
            public ClickSpan convert(URLSpan span, ClickSpan.OnClickListener listener) {
                return (new ClickSpan(span.getURL(), listener));
            }
       }
    
  4. Usage

    TextView textView = ((TextView) this.findViewById(R.id.your_id));
    textView.setText("your_text");
    Linkify.addLinks(contentView, Linkify.ALL);
    
    Spannable formattedContent = UIUtils.RichTextUtils.replaceAll((Spanned)textView.getText(), URLSpan.class, new UIUtils.URLSpanConverter(), new UIUtils.ClickSpan.OnClickListener() {
    
        @Override
        public void onClick(String url) {
            // Call here your Activity
        }
    });
    textView.setText(formattedContent);
    
请注意,我在这里将所有类定义为静态的,这样您就可以直接将其放入您的Util类中,并且可以轻松地从任何地方引用它。

2
谢谢,希望这个答案能够帮助某些人更好地理解流程,而不是简单地复制粘贴并忘记。后者是我喜欢CommonsWare明智地省略ClickableSpan子类的完整实现的原因。 - A-Live

2

我想分享一个与之相对应的解决方案,使用我刚刚创建的Textoo

    TextView yourTextView = Textoo
        .config((TextView) findViewById(R.id.your_text_view))
        .addLinksHandler(new LinksHandler() {
            @Override
            public boolean onClick(View view, String url) {
                if (showInMyWebView(url)) {
                    //
                    // Your custom handling here
                    //
                    return true;  // event handled
                } else {
                    return false; // continue default processing i.e. launch browser app to display link
                }
            }
        })
        .apply();

在底层,该库会将TextView中的URLSpan替换为自定义ClickableSpan实现,以将点击事件分派给用户提供的LinksHandler。这种机制与@commonsWare的解决方案非常相似,只是打包成更高级别的API,使其更易于使用。

0

我认为David Hedlund自问自答的回答是正确的方法。在我的情况下,我有一个包含用户收件箱中短信内容的TextView,并且我想要处理以下行为:

  1. 如果找到WEB URL,则将其链接化并在应用程序内处理单击。
  2. 如果找到电话号码,则将其链接化并处理单击,显示选择器让用户选择是否拨打该号码或将其添加到通讯录(全部在应用程序内完成)

为了实现这一点,我以如下方式使用了链接的答案:

TextView tvBody = (TextView)findViewById(R.id.tvBody);
tvBody.setText(messageContentString);

// now linkify it with patterns and scheme
Linkify.addLinks(tvBody, Patterns.WEB_URL, "com.my.package.web:");
Linkify.addLinks(tvBody, Patterns.PHONE, "com.my.package.tel:");

现在在清单中:

<activity
    android:name=".WebViewActivity"
    android:label="@string/web_view_activity_label">
    <intent-filter>
        <category android:name="android.intent.category.DEFAULT" />
        <action android:name="android.intent.action.VIEW"/>
        <data android:scheme="com.duckma.phonotto.web"/>
    </intent-filter>
</activity>
...
<activity
    android:name=".DialerActivity"
    android:label="@string/dialer_activity_label">
    <intent-filter>
        <category android:name="android.intent.category.DEFAULT" />
        <action android:name="android.intent.action.VIEW"/>
        <data android:scheme="com.duckma.phonotto.tel"/>
    </intent-filter>
</activity>
<activity
    android:name=".AddressBook"
    android:label="@string/address_book_activity_label">
    <intent-filter>
        <category android:name="android.intent.category.DEFAULT" />
        <action android:name="android.intent.action.VIEW"/>
        <data android:scheme="com.duckma.phonotto.tel"/>
    </intent-filter>
</activity>

这对我来说完全没有问题,当用户点击电话号码时,选择器将显示拨号器/通讯录选项。在被调用的活动中使用 getIntent().getData() 查找传递的带有数据的 Url。


1
虽然对某些人来说可能是一个优势,但我们始终启动活动的方式在这里是一个限制因素,而自定义跨度让我们以任何想要的方式处理链接。另一个问题是,我们需要重新发明一个轮子来使用 Linkify 的替代方案,并考虑到原始文本可能包含可识别 HTML 部分,我们需要相当复杂的逻辑才能避免 Html.fromHtml 破坏它。 - A-Live
我明白了,实际上它更适合我的情况,因为我将其用于短信内容,很少包含HTML标记。 - Carlo Moretti

-3

然后这样做:

        TextView textView=(TextView) findViewById(R.id.link);
        textView.setOnClickListener(new OnClickListener() {

            public void onClick(View v) {
                Intent intent=new Intent(YourActivityName.this,WebActivity.class);
                startActivity(intent);
            }
        });

WebActivity类的onCreate方法:
WebView webView=(WebView) findViewById(R.id.web);
webView.loadUrl("http://www.google.co.in");

在xml文件中的TextView标签下添加以下内容:

android:clickable="true"

1
正如A-Live所说:“textView中有很多文本,不仅仅是链接。”因此,这种方法不适合他的需求。 - Tamás Szincsák
@AkashG,你能否请删除这个回答 - 它与问题无关。提前致谢。 - A-Live

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接