Bestimmen, welches Wort in einer Android-Textansicht angeklickt wird

Grundsätzlich möchte ich eine Textpassage (möglicherweise einen ziemlich langen Text) anzeigen und dem Benutzer erlauben, auf ein beliebiges Wort zu klicken. An diesem Punkt möchte ich feststellen, auf welches Wort sie geklickt haben. Ich möchte auch den gesamten Satz erhalten, in dem das Wort vorkommt (das ist ziemlich trivial, vorausgesetzt, ich kann bestimmen, an welcher Position sich das Wort im Text befindet).

Im Idealfall würde ich einfach auf ein onTouch-Ereignis warten, mir das X und Y holen und so etwas sagentextView.wordAt(event.x, event.y) odertextView.cursorPositionNearest(event.x, event.y)aber es scheint nicht so einfach zu sein :-)

Meine derzeitige größte Anstrengung ist die Verwendung von aTextvorschau und eine erstellenClickableSpan pro Wort. Es funktioniert, aber es ist nicht gerade elegant, und ich denke, es würde anfangen, Gedächtnis zu verlieren, wenn ich es für längere Texte verwende.

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();
    }
}

Wenn jemand eine bessere Idee hat, würde ich sie gerne hören.

Danke im Voraus, Dave

Ich bin mir nicht sicher, wie ich wirklich Fragen zum Stackoverflow beantworten soll, aber ich habe es geschafft, Code aus der Android 15-API zu rippen und ihn geringfügig zu ändern, um das zu tun, was ich brauchte. Vielen Dank an Dheeraj für den Vorschlag.

Mit dem neuen Code kann ich eine Caret-Position basierend auf einer Berührungsereignisposition abrufen. Von dort aus sollte ich in der Lage sein, das Wort, das berührt wurde, und den Satz, in dem es erscheint, abzurufen. Code im Anhang:

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);
}

Antworten auf die Frage(1)

Ihre Antwort auf die Frage