24

基本的に、私はテキストの一節(潜在的にはかなり長いテキスト)を表示し、ユーザーが任意の単語をクリックできるようにしたいです。その時点で、彼らがクリックした単語を特定したいと思います。また、単語が表示されている文全体を取得したいと思います(これは、単語がテキスト内のどの位置にあるかを判断できると仮定すると、非常に簡単です)。

理想的には、onTouchイベントをリッスンし、XとYを取得して、textView.wordAt(event.x, event.y)またはtextView.cursorPositionNearest(event.x, event.y)のようなことを言うだけですが、それほど簡単ではないようです:-)

私の現在の最善の努力は、TextViewを使用し、単語ごとに1つのClickableSpanを作成することです。動作しますが、正確にはエレガントではありません。長いテキストで使用すると、メモリを消費し始めると思います。

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

誰かがもっと良いアイデアを持っているなら、私はそれを聞いてみたいです。

よろしくお願いします、デイブ

スタックオーバーフローに関する質問に実際にどのように答えるべきかわかりませんが、Android 15 APIからいくつかのコードを取り除いて、必要なことを実行するために少し変更することができました。提案してくれたDheerajに感謝します。

新しいコードを使用すると、タッチイベントの位置に基づいてキャレットの位置を取得できます。そこから、タッチされた単語とそれに表示される文を取得できるはずです。添付されているコード:

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

1 に答える 1

14

APIレベル14以上を対象としている場合は、を使用できますgetOffsetForPosition()

以前のAndroidバージョンについては、このメソッドのコードをAndroidソースからコピーして、独自のTextViewを拡張できるかどうかを確認してください。

于 2012-07-22T16:57:44.627 に答える