Открыть ссылки 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"