Determinar qué palabra se hace clic en una vista de texto de Android

Básicamente, quiero mostrar un pasaje de texto (potencialmente, un texto bastante largo) y permitir que el usuario haga clic en cualquier palabra. En ese momento, quiero determinar en qué palabra hicieron clic. También quiero obtener la frase completa en la que aparece la palabra (esto es bastante trivial, asumiendo que puedo determinar en qué posición se encuentra la palabra dentro del texto).

Lo ideal sería escuchar un evento onTouch, obtener la X y la Y, y decir algo comotextView.wordAt(event.x, event.y) otextView.cursorPositionNearest(event.x, event.y), pero parece que no es tan fácil :-)

Mi mejor esfuerzo actual consiste en usar unVista de texto y creando unoClickableSpan por palabra Funciona, pero no es exactamente elegante, y supongo que comenzaría a comerme memoria si lo uso en textos más largos.

private final String text = "This is the text";
private TextView textView;

@Override
public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_text_view);

    textView = (TextView) findViewById(R.id.text_view);

    SpannableString ss = new SpannableString(text);
    //  create spans for "this", "is", "the" and "text"
    ss.setSpan(new IndexedClickableSpan(0, 4), 0, 4, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
    ss.setSpan(new IndexedClickableSpan(5, 7), 5, 7, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
    ss.setSpan(new IndexedClickableSpan(8, 11), 8, 11, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
    ss.setSpan(new IndexedClickableSpan(12, 16), 12, 16, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);

    textView.setText(ss);
}

private final class IndexedClickableSpan extends ClickableSpan {

    int startIndex, endIndex;

    public IndexedClickableSpan(int startIndex, int endIndex) {
        this.startIndex = startIndex;
        this.endIndex = endIndex;
    }

    @Override
    public void onClick(View widget) {
        String word = TextViewActivity.this.text.substring(startIndex, endIndex);
        Toast.makeText(TextViewActivity.this, "You clicked on " + word, Toast.LENGTH_SHORT).show();
    }
}

Si alguien tiene una idea mejor, me encantaría escucharla.

Gracias de antemano, Dave

No estoy seguro de cómo se supone que debo responder a las preguntas en el stackoverflow, pero he logrado extraer algo de código de la API de Android 15 y modificarlo muy ligeramente para hacer lo que necesitaba. Gracias a Dheeraj por la sugerencia.

El nuevo código me permite obtener una posición de intercalación basada en una posición de evento táctil, desde allí debería poder obtener la palabra que fue tocada y la frase en la que aparece.

public int getOffsetForPosition(TextView textView, float x, float y) {
    if (textView.getLayout() == null) {
        return -1;
    }
    final int line = getLineAtCoordinate(textView, y);
    final int offset = getOffsetAtCoordinate(textView, line, x);
    return offset;
}

private int getOffsetAtCoordinate(TextView textView2, int line, float x) {
    x = convertToLocalHorizontalCoordinate(textView2, x);
    return textView2.getLayout().getOffsetForHorizontal(line, x);
}

private float convertToLocalHorizontalCoordinate(TextView textView2, float x) {
    x -= textView2.getTotalPaddingLeft();
    // Clamp the position to inside of the view.
    x = Math.max(0.0f, x);
    x = Math.min(textView2.getWidth() - textView2.getTotalPaddingRight() - 1, x);
    x += textView2.getScrollX();
    return x;
}

private int getLineAtCoordinate(TextView textView2, float y) {
    y -= textView2.getTotalPaddingTop();
    // Clamp the position to inside of the view.
    y = Math.max(0.0f, y);
    y = Math.min(textView2.getHeight() - textView2.getTotalPaddingBottom() - 1, y);
    y += textView2.getScrollY();
    return textView2.getLayout().getLineForVertical((int) y);
}

Respuestas a la pregunta(1)

Su respuesta a la pregunta