0

I have got a textView to which I do this:

textView.setLineSpacing(1f, .70f);

and then I would like to set a background color only to specific words in that textView so I've tried this:

spannableStringBuilder.setSpan(new BackgroundColorSpan(bckgndColor), spanStart, spanEnd, 0);

The problem is that because of the lineSpacing it shows up like this:

enter image description here

as opposed to if I were to comment out the lineSpacing() line I would have this, which is perfect:

enter image description here

Any ideas on how to solve this problem? I tried playing around with the BackgroundColorSpan object, but after digging in the code of the class I see that it only does this:

   /**
     * Updates the background color of the TextPaint.
     */
    @Override
    public void updateDrawState(@NonNull TextPaint textPaint) {
        textPaint.bgColor = mColor;
    }

and even if I override the class, I don't have access to any "Rect" value to adjust or ... dunno..

Any ideas are appreciated. Thank you !

AndreiBogdan
  • 10,858
  • 13
  • 58
  • 106
  • Looks like you might have to implement a wholly custom span. That background draw is handled in `TextLine`, and no adjustments are made for line spacing, AFAICT. It always draws that background rectangle from the top of the current line's bounds to the top of the next line's, which is why the regular spans extend too far at the top and are cut off at the bottom. – Mike M. Jul 06 '22 at 14:59
  • I'm not sure how you want to adjust those backgrounds, exactly, but [here's a simple example](https://drive.google.com/file/d/12euqhyzS_nJ44EAR9WTTSKzCGMWgW-df/view?usp=sharing) that "shrinks" it to the actual text bounds. Looks like: https://i.stack.imgur.com/iIdTl.png. It's kind of abusing a `ReplacementSpan`, since we're not really replacing anything, but which seems to be about the only kind we can use for something like this. It won't work correctly with certain kinds of spans that might overlap this custom one, but that doesn't seem to be needed here. – Mike M. Jul 06 '22 at 16:16
  • @MikeM. Please write your answer, perhaps the entire code as a response. It's EXACTLY what I am looking for !!!! Thank you !!! – AndreiBogdan Jul 06 '22 at 16:25
  • 1
    No problem! I don't post answers here any more, however, so please feel free to finish up this question however you like. You're welcome to copy/paste any or all of my example and image here, if you decide to post an answer. Thank you, though. I appreciate the offer. Glad I could help. Cheers! – Mike M. Jul 06 '22 at 16:56

1 Answers1

0

With help from MikeM. this is what I was looking for:

public class FauxBackgroundColorSpan extends ReplacementSpan {

    private final Rect tmpBounds = new Rect();
    private final int backgroundColor;

    public FauxBackgroundColorSpan(@ColorInt int color) {
        backgroundColor = color;
    }

    @Override
    public int getSize(Paint paint, CharSequence text, int start,
                       int end, Paint.FontMetricsInt fm) {
        // Necessary for full length spans to be drawn.
        if (fm != null) {
            final Paint.FontMetricsInt pfm = paint.getFontMetricsInt();
            fm.top = pfm.top;
            fm.ascent = pfm.ascent;
            fm.descent = pfm.descent;
            fm.bottom = pfm.bottom;
        }

        // This would normally be the width of whatever we're replacing the text with, but
        // we just return the text's own measure, since we're not really replacing anything.
        return getTextMeasure(paint, text, start, end);
    }

    @Override
    public void draw(Canvas canvas, CharSequence text, int start,
                     int end, float x, int top, int y, int bottom, Paint paint) {
        // Draw the background.
        final int previousColor = paint.getColor();
        final Paint.Style previousStyle = paint.getStyle();
        paint.setColor(backgroundColor);
        paint.setStyle(Paint.Style.FILL);

        final Rect bounds = tmpBounds;
        paint.getTextBounds(text.toString(), start, end, bounds);

        canvas.drawRect(x, y + bounds.top,
                x + getTextMeasure(paint, text, start, end), y + bounds.bottom, paint);

        paint.setStyle(previousStyle);
        paint.setColor(previousColor);

        // Draw the text we "replaced".
        canvas.drawText(text, start, end, x, y, paint);
    }

    private int getTextMeasure(Paint paint, CharSequence text, int start, int end) {
        return (int) (paint.measureText(text, start, end) + .5f);
    }
}
AndreiBogdan
  • 10,858
  • 13
  • 58
  • 106