【问题标题】:Open TextView links at another activity, not default browser在另一个活动中打开 TextView 链接,而不是默认浏览器
【发布时间】:2012-07-09 22:25:43
【问题描述】:

autoLinkMask 的textView 设置为Linkify.ALL,我可以打开链接并且浏览器会显示网页。

我需要调用另一个活动来完成它的 webView 而不离开应用程序。

重要提示:

  • 更改 textView 内容不是一种选择,我需要让链接按照它们所拥有的方案显示,
  • textView 中有很多文本,不仅仅是链接。

我浏览了movementMethodIntentFilters,可能会漏掉一些东西,但看起来无济于事。

那么,有什么选项可以拦截TextView 中的触摸链接以对其进行操作而不打开浏览器?

如果您想提及this SO 问题,请给出一些论据,为什么它似乎无法解决与我相同的问题。

【问题讨论】:

  • 您链接到的 SO 问题有一个正确答案的胆量:将 Linkify 生成的 URLSpan 对象替换为您自己的自定义 ClickableSpan,它可以满足您的需求。
  • 您想在网页视图中打开另一个文本点击活动的链接??
  • @CommonsWare 会尽快检查,谢谢您的指点。
  • @AkashG 确切地说,我想获取触摸的 url 并在另一个活动中使用它以在 web 视图中显示。将保持应用程序风格稳固,我真的不想受到责备,而不是用户自己配置了他们喜欢的自定义浏览器。
  • @CommonsWare 是的,它可以工作,缺点是我必须手动找到所有链接并将它们标记为可点击,并且必须改进解决方案以将点击的文本传递给 onClick 事件,我喜欢的是它也可以链接标签!请改写您的评论作为答案,先生,非常感谢您。

标签: android textview linkify


【解决方案1】:

第 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 对象。

【讨论】:

  • 这使得解决方案如此完整,再次感谢。我还发现它比上面 cmets 中讨论的另一个解决方案更好,实际上看起来超级酷。
  • @A-Live 我遵循了这个解决方案,但 textview 中的链接不可点击。我尝试切换 android:linksClickable 并为 android:autoLink 尝试了不同的值。想法?
  • @Ravi 我不知道你到底是什么意思,或者什么是帮助你的最佳方式,创建一个单独的问题并尽可能多地提供细节必须有路要走。跨度>
  • 此解决方案不仅适用于常规使用,还适用于无障碍模式下的对讲:)
【解决方案2】:

我通过添加 Click Listener 使 @CommonsWare 的代码更加优雅和清晰,它可以直接添加到替换 URL Spans 的相同方法中。

  1. 定义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. 定义RichTextUtils Class,它将操作 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. 定义URLSpanConverter class,它将使用您的自定义跨度执行替换 URLSpan 的实际代码。

        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. 用法

    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 类中,然后从任何地方轻松引用它。

【讨论】:

  • 谢谢,希望答案能帮助人们更好地理解流程,而不是简单地复制粘贴忘记,后者就是为什么我喜欢 CommonsWare 明智地省略了 ClickableSpan 的完整实现的方式子类。
【解决方案3】:

只是为了分享一个使用我刚刚创建的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(s)。该机制与@commonsWare 的解决方案非常相似。只需封装在更高级别的 API 中,使其更易于使用。

【讨论】:

    【解决方案4】:

    我认为David Hedlund's answer to himself 是要走的路。在我的情况下,我有一个包含 SMS 内容表单用户收件箱的 TextView,我想处理休闲行为:

    1. 如果找到 WEB URL,则链接它并处理应用程序内的点击。
    2. 如果找到 PHONE NUMBER,则链接它并处理显示选择器的点击,让用户选择是拨打该号码还是将其添加到地址簿(全部在应用程序中)

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

    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。

    【讨论】:

    • 虽然这对某些人来说可能是一个优势,但我们总是开始一个活动的方式是一个限制因素,而自定义跨度让我们可以以任何我们想要的方式处理链接。另一个问题是我们需要用Linkify 替代方案重新发明一个轮子,并且考虑到原始文本可能包含 html 可识别的部分,我们需要一个相当复杂的逻辑来不让Html.fromHtml 破坏它。跨度>
    • 我明白了,事实上它更适合我将它用于很少包含 html 标记的 SMS 内容的情况。
    【解决方案5】:

    比这样做:

            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"
    

    【讨论】:

    • 正如 A-Live 所写:“textView 中有很多文本,而不仅仅是链接。”。所以这种方法不适合他的需要。
    • @AkashG 你能删除这个答案吗 - 它与问题无关。提前致谢。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-07-18
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-05-22
    • 2011-07-26
    相关资源
    最近更新 更多