Как использовать кликабельные и кастомные спаны в Android
Что проверяют
Знаете ли вы, как устроена разметка текста в TextView, и доводили ли кликабельный текст до рабочего состояния, с доступностью и списками. Следом обычно спрашивают, как сделать то же в Compose и почему клик по ссылке ломает клик по элементу списка.
Как отвечать
Основа: размеченный текст это SpannableString или SpannableStringBuilder, а спан вешается через setSpan(what, start, end, flags). В Kotlin удобнее buildSpannedString { inSpans(...) { append(...) } } из core-ktx: индексы не приходится считать вручную.
- флаги:
SPAN_EXCLUSIVE_EXCLUSIVEдля статичного текста. Инклюзивные флаги важны вEditText, где они решают, растянется ли спан на текст, введённый на его границе; - кликабельный спан: наследник
ClickableSpanсonClick, внешний вид правится вupdateDrawState. Клики заработают только послеtextView.movementMethod = LinkMovementMethod.getInstance(), иначе спан просто нарисуется; - свои спаны: для цвета и фона хватает
CharacterStyleсUpdateAppearance. Если меняется размер или шрифт, нуженMetricAffectingSpan, иначе строка не перемеряется.ReplacementSpanсам рисует фрагмент целиком, например чип или бейдж, а абзацные спаны вродеLeadingMarginSpanдействуют на весь абзац; - доступность: TalkBack с Android 8 показывает ссылки из
ClickableSpanв своём меню, для старых версий естьViewCompat.enableAccessibleClickableSpanSupport. Текст ссылки должен быть осмысленным, а не «здесь».
В Compose ClickableText объявлен устаревшим, ссылки делают через AnnotatedString с LinkAnnotation.Url или LinkAnnotation.Clickable и функцией withLink, доступность там встроена.
Где спотыкаются
Считают индексы руками и промахиваются на символ. Надёжнее искать подстроку через indexOf или собирать текст билдером.
Не знают, что LinkMovementMethod перехватывает все касания по TextView. В ячейке RecyclerView нажатие мимо ссылки перестаёт доходить до элемента, а после клика остаётся подсветка, которую убирают через highlightColor.
Отвечают только про View. На проекте с Compose ждут LinkAnnotation, а ClickableText в роли основного способа выдаёт устаревшие знания.