Weird behavior of Layout.getPrimaryHorizontal: values are off by ~28 pixels

Viewed 38

I'm trying to detect if a user clicked on an ImageSpan. I use Layout.getPrimaryHorizontal(offset) to get the position of ImageSpan within the TextView but get a value which is off by approximately -28 pixels.

Here is the simplified code:

SpannableStringBuilder result = new SpannableStringBuilder();

// Insert Play icon
Drawable play = AppCompatResources.getDrawable(ctx, R.drawable.ic_play);
ImageSpan span = new ImageSpan(play, ImageSpan.ALIGN_BOTTOM);
final Rect bounds = span.getDrawable().getBounds();
result.append(" ", span, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);

result.append(" x "); // The "x" is just for illustrative purposes.

// Insert waveform.
WaveDrawable dw = new WaveDrawable(new Rect(0, 0, (int) width, (int) (bounds.height() * 0.9f)));
ImageSpan wave = new ImageSpan(dw, ImageSpan.ALIGN_BASELINE);
result.append(new SpannableStringBuilder().append(" ", wave, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE), new ClickableSpan() {
     @Override
     public void onClick(@NonNull View widget) {
          TextView tv = (TextView) widget;
          Layout tvl = tv.getLayout();
          Spanned fullText = (Spanned) tv.getText();
          float startX = tvl.getPrimaryHorizontal(fullText.getSpanStart(wave));
          float endX = tvl.getPrimaryHorizontal(fullText.getSpanEnd(wave));
          Log.i(TAG, "Waveform clicked! start=" + startX +
                            "; end=" + endX +
                            "; width=" + (endX - startX) +
                            "; TextView width=" + widget.getWidth() +
                            "; drawable width=" + dw.getBounds().width());
     }
}, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);

The logcat output is

Waveform clicked! start=138.0; end=567.0; width=429.0; TextView width=601; drawable width=429

The values start=138.0; end=567.0 are definitely wrong, they should be around start=166.0; end=595.0;

Here is what it looks like:

enter image description here

The thin red box is the boundary of the drawable. The white background is the TextView. It's very obvious from the picture that the space between the right edges of the drawable and the TextView is around 6 pixels, not 601-567 = 34 pixels like getPrimaryHorizontal returns. For scale: the size of the Play button is 99 pixels.

If I use the same approach to get the position of the Play ImageSpan I get correct result.

Is this an Android bug or my bug?

1 Answers

I eventually figured it out: the TextView had padding. Layout coordinates do not account for padding, while TextView coordinates do.

Related