Android: Como determinar o índice de caracteres da posição de um evento de toque no TextView?
-
21-09-2019 - |
Pergunta
eu tenho um TextView
com um OnTouchListener
. O que eu quero é o índice de personagem que o usuário está apontando quando eu conseguir o MotionEvent
. Existe alguma maneira de chegar às métricas de fonte subjacentes do TextView
?
Solução
Não estou ciente de uma maneira simples direta de fazer isso, mas você deve poder montar algo usando o Pintar objeto da visão de texto por meio de uma chamada para TextView.getPaint ()
Depois de ter o objeto de tinta, você terá acesso aos fontmétricos subjacentes por meio de uma chamada para pintar.getFontMetrics () e ter acesso a outras funções como o pinting.measureTeText () pint.getTextBounds () e pint.getTextWidths () para acessar o Tamanho real do texto exibido.
Outras dicas
Você já tentou algo assim:
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
}
Espero que isto ajude...
Embora geralmente funcione, tive alguns problemas com a resposta de Tony Blues.
Primeiramente getOffsetforHorizontal Retorna um deslocamento, mesmo que a coordenada X esteja muito além do último caractere da linha.
Em segundo lugar, o deslocamento do personagem retornado às vezes pertence ao próximo personagem, não ao personagem diretamente abaixo do ponteiro. Aparentemente, o método retorna o deslocamento da posição mais próxima do cursor. Isso pode estar à esquerda ou à direita do personagem, dependendo do que está mais próximo.
Minha solução usa getPrimaryHorizontal Em vez disso, determinar a posição do cursor de um determinado deslocamento e usa pesquisa binária para encontrar o deslocamento sob a coordenada X do ponteiro.
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;
}
Editar: Esta versão atualizada funciona melhor com quebras de linha não naturais, quando uma palavra longa não se encaixa em uma linha e se divide em algum lugar no meio.
Advertências: em textos hifenizados, clicando no hífen no final de uma linha retorna o índice do caractere ao lado. Além disso, esse método não funciona bem com textos RTL.