HTML с кликабельными ссылками и выделением текста в TextView

Когда вы хотите нажать на ссылку внутри 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);
  } 
}

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *