У меня есть TextView с OnTouchListener. Мне нужен индекс символа, на который указывает пользователь, когда я получаю MotionEvent. Есть ли способ получить базовые метрики шрифта TextView?
Android: как определить индекс символа позиции сенсорного события в TextView?
Ответы (3)
Я не знаю простого прямого способа сделать это, но вы сможете собрать что-то вместе, используя Рисовать объект TextView с помощью вызова TextView.getPaint()
Когда у вас есть объект рисования, у вас будет доступ к базовым FontMetrices через вызов Paint.getFontMetrics() и доступ к другим функциям, таким как Paint.measureText(), Paint.getTextBounds() и Paint.getTextWidths() для доступа к реальный размер отображаемого текста.
Вы пробовали что-то вроде этого:
Layout layout = this.getLayout();
if (layout != null)
{
int line = layout.getLineForVertical(y);
int offset = layout.getOffsetForHorizontal(line, x);
// At this point, "offset" should be what you want - the character index
}
Надеюсь это поможет...
Хотя в целом это работает, у меня было несколько проблем с ответом Тони Блюза.
Во-первых, getOffsetForHorizontal возвращает смещение даже если координата x выходит далеко за последний символ строки.
Во-вторых, возвращаемое смещение символа иногда относится к следующему символу, а не к символу непосредственно под указателем. По-видимому, метод возвращает смещение ближайшей позиции курсора. Это может быть слева или справа от персонажа, в зависимости от того, что находится ближе.
В моем решении вместо определения курсора используется getPrimaryHorizontal. положение определенного смещения и использует двоичный поиск, чтобы найти смещение под координатой x указателя.
public static int getCharacterOffset(TextView textView, int x, int y) {
x += textView.getScrollX() - textView.getTotalPaddingLeft();
y += textView.getScrollY() - textView.getTotalPaddingTop();
final Layout layout = textView.getLayout();
final int lineCount = layout.getLineCount();
if (lineCount == 0 || y < layout.getLineTop(0) || y >= layout.getLineBottom(lineCount - 1))
return -1;
final int line = layout.getLineForVertical(y);
if (x < layout.getLineLeft(line) || x >= layout.getLineRight(line))
return -1;
int start = layout.getLineStart(line);
int end = layout.getLineEnd(line);
while (end > start + 1) {
int middle = start + (end - start) / 2;
if (x >= layout.getPrimaryHorizontal(middle)) {
start = middle;
}
else {
end = middle;
}
}
return start;
}
Редактировать: эта обновленная версия лучше работает с неестественными разрывами строк, когда длинное слово не помещается в строку и разбивается где-то посередине.
Предостережения: в текстах с дефисом нажатие на дефис в конце строки возвращает индекс символа рядом с ним. Также этот метод плохо работает с текстами RTL.