Когда вы хотите нажать на ссылку внутри TextView, не перенаправляя её во внешний браузер, или установить Spannable с HTML в TextView, то в большинстве случаев достаточно кастомизировать LinkMovementMethod, чтобы сделать ссылку кликабельной, и затем установить его в TextView.
setMovementMethod(LinkMovementMethod.getInctance());
Но LinkMovementMethod по умолчанию не поддерживает выделение текста, поэтому придётся переопределить несколько методов, чтобы добиться выделения текста.
public class CustomMovementMethod extends LinkMovementMethod {
@Override
public boolean canSelectArbitrarily () {
return true;
}
@Override
public void initialize(TextView widget, Spannable text) {
Selection.setSelection(text, text.length());
}
@Override
public void onTakeFocus(TextView view, Spannable text, int dir) {
if ((dir & (View.FOCUS_FORWARD | View.FOCUS_DOWN)) != 0) {
if (view.getLayout() == null) {
// This shouldn't be null, but do something sensible if it is.
Selection.setSelection(text, text.length());
}
} else {
Selection.setSelection(text, text.length());
}
}
}
При использовании приведённого выше кода однако вы не сможете сделать выделение текста, так как после скролла текста выделение будет потеряно.
Поэтому существует ещё один MovementMethod, который называется ArrowKeyMovementMethod.
И LinkMovementMethod, и ArrowKeyMovementMethod являются расширениями интерфейса MovementMethod. Но ArrowKeyMovementMethod, в отличие от предыдущего, хорошо реализовывает функционал выделения текста.
Поэтому, если вы хотите кастомизировать MovementMethod у TextView для поддержки кликабельных ссылок, то лучше расширить класс ArrowKeyMovementMethod, а не LinkMovementMethod. В этом случае вам также не придётся переопределять вышеупомянутые методы, такие как onTakeFocus(), потому что это уже сделано под капотом класса.
public class CustomMovementMethod extends ArrowKeyMovementMethod {
// Контекст, который передаём в класс
private static Context movementContext;
// Инстанс CustomMovementMethod
private static CustomMovementMethod linkMovementMethod = new CustomMovementMethod();
public static MovementMethod getInstance(Context c){
// Устанавливаем контекст
movementContext = c;
// Возвращаем инстанс
return linkMovementMethod;
}
public boolean onTouchEvent(TextView widget, Spannable buffer, MotionEvent event){
// Получаем событие у MotionEvent
int action = event.getAction();
// Если событие равно MotionEvent.ACTION_UP
if(action == MotionEvent.ACTION_UP) {
// Определяем координаты нажатия
int x = (int) event.getX();
int y = (int) event.getY();
x -= widget.getTotalPaddingLeft();
y -= widget.getTotalPaddingTop();
x += widget.getScrollX();
y += widget.getScrollY();
// Определяем URL
Layout layout = widget.getLayout();
int line = layout.getLineForVertical(y);
int off = layout.getOffsetForHorizontal(line, x);
// Находим URL, который был нажат
URLSpan[] link = buffer.getSpans(off, off, URLSpan.class);
// Если мы нашли URL
if (link.length != 0) {
// Найденный URL
String url = link[0].getURL();
// Если URL корректный
if (url.contains("https") | url.contains("tel") | url.contains("mailto") | url.contains("http") | url.contains("https") | url.contains("www")){
// Открываем URL в браузере
Uri uri = Uri.parse(url);
movementContext.startActivity(new Intent(Intent.ACTION_VIEW, uri));
}
return true;
}
}
return super.onTouchEvent(widget, buffer, event);
}
}