Открыть ссылки TextView в другом действии, а не в браузере по умолчанию

Имея textView с autoLinkMask, установленным на Linkify.ALL, я могу открыть ссылки, а браузер показывает веб-страницу.

Мне нужно вызвать другое действие, которое сделает это с помощью webView, не выходя из приложения.

Важные замечания:

  • изменение содержимого textView не является вариантом, мне нужно, чтобы ссылки отображались так, как они есть с их схемами,
  • есть много текста в textView, а не только ссылка.

Я просмотрел movementMethod и IntentFilters, может пропустить что-то, но похоже, что это не поможет.

Итак, любая возможность перехватить затронутую ссылку в TextView, чтобы что-то сделать с ней, не открывая браузер?

Если вы хотите упомянуть вопрос this SO, приведите несколько аргументов, почему причина не в том, чтобы решить ту же проблему, что и у меня.

Ответ 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.

Ответ 2

Я делаю код @CommonsWare более изящным и понятным, добавляя Click Listener, который можно добавить непосредственно в тот же метод, который заменяет URL Spans.

  • Определите 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);
        }
    }
    
  • Определите RichTextUtils Class, который будет обрабатывать Spanned text вашего 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);
    
         }
    }
    
  • Определите 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));
            }
       }
    
  • Использование

    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, а затем легко ссылаться на него из любого места.

Ответ 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();

Под капотом библиотека заменит URLSpan в TextView с помощью специальной реализации ClickableSpan, которая отправляет события щелчка на предоставленный пользователем LinksHandler (s). Механизм очень похож на решение от @commonsWare. Просто установите пакет в API более высокого уровня, чтобы упростить его использование.

Ответ 4

Я думаю, Дэвид Хедлунд, чтобы ответить самому себе - это путь. В моем случае у меня был входящий почтовый ящик формы TextView, содержащий SMS-контент, и я хотел обработать следующее поведение:

  • Если найден URL-адрес WEB, привяжите его и обработайте клик внутри приложения.
  • Если найден НОМЕР ТЕЛЕФОНА, затем привяжите его и обработайте клик, показывая выборщик, чтобы позволить пользователю выбрать, позвонить ли номер или добавить его в адресную книгу (все в приложении).

Чтобы достичь этого, я использовал связанный ответ следующим образом:

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 с данными в нем.

Ответ 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);
            }
        });

onСоздание класса WebActivity:

WebView webView=(WebView) findViewById(R.id.web);
webView.loadUrl("http://www.google.co.in");

Добавьте это в textview в xml:

android:clickable="true"