title: Alternative to ReplacementSpan in Android
body: <p>I have an app that pages large text and sets multiple spans to each word or sentence. I am using ReplacementSpan to draw the background for each word. I cannot use BackgroundSpan because it is too simple and doesn't give me control over the canvas. Because of ReplacementSpan extending MetricAffectingSpan which affects the layout of the text, breaking my paging completely. I am using StaticLayout to calculate the text for each page, and StaticLayout doesn't allow spanning so it can calculate a priori the spanning size influences.</p>
  
  <p>Is there a replacement to ReplacementSpan? How can I draw the background I want without affecting the size and layout of the text itself?</p>
  
  <p>This is the code for my replacementspan:</p>
  
  <pre><code>public class BackgroundColorWithoutLineHeightSpan extends ReplacementSpan {
  
    private static final float DP_ACTIVE = ViewsUtils.dpToPx(4);
    private static final int DP_OUTSIDE_PADDING = (int) ViewsUtils.dpToPx(6);
    private static final float DP_PHRASE = ViewsUtils.dpToPx(4);
    private static final float DP_ROUNDED = ViewsUtils.dpToPx(3);
  
    private final int mColor;
    private final int mTextHeight;
    private int mBorderColor;
    private boolean mIsSelected;
    private boolean mIsPhrase;
  
    public BackgroundColorWithoutLineHeightSpan(int color, int textHeight, boolean isPhrase) {
      mColor = color;
      mTextHeight = textHeight;
      mIsPhrase = isPhrase;
    }
  
    public BackgroundColorWithoutLineHeightSpan(int color, int textHeight, boolean isSelected, int borderColor, boolean isPhrase) {
      mColor = color;
      mTextHeight = textHeight;
      mIsSelected = isSelected;
      mBorderColor = borderColor;
      mIsPhrase = isPhrase;
    }
  
    @Override
    public int getSize(@NonNull Paint paint, CharSequence text, int start, int end, Paint.FontMetricsInt fm) {
      return Math.round(measureText(paint, text, start, end));
    }
  
    @Override
    public void draw(@NonNull Canvas canvas, CharSequence text, int start, int end, float x, int top, int y, int bottom, Paint paint) {
  
      canvas.save();
  
      Rect newRect = canvas.getClipBounds();
      newRect.inset(-DP_OUTSIDE_PADDING, -DP_OUTSIDE_PADDING);
  
      canvas.clipRect(newRect, Region.Op.REPLACE);
  
      float measuredText = measureText(paint, text, start, end);
  
      int paintColor = paint.getColor();
  
      if (!mIsSelected) {
        RectF rect;
        rect = new RectF(x, top, x + measuredText, top + mTextHeight);
  
        paint.setStrokeWidth(0.0f);
        paint.setColor(mColor);
        paint.setStyle(Paint.Style.FILL);
  
        canvas.drawRoundRect(rect, DP_ROUNDED, DP_ROUNDED, paint);
  
      } else {
  
        RectF rect;
        if (mIsPhrase) {
          rect = new RectF(x - DP_PHRASE, top - DP_PHRASE, x + measuredText + DP_PHRASE, top + mTextHeight + DP_PHRASE);
        } else {
          rect = new RectF(x - DP_ACTIVE, top - DP_ACTIVE, x + measuredText + DP_ACTIVE, top + mTextHeight + DP_ACTIVE);
        }
        paint.setStrokeWidth(0.0f);
        paint.setColor(mColor);
        paint.setStyle(Paint.Style.FILL);
  
        canvas.drawRoundRect(rect, DP_ROUNDED, DP_ROUNDED, paint);
  
        RectF border;
        if (mIsPhrase) {
          border = new RectF(x - DP_PHRASE, top - DP_PHRASE, x + measuredText + DP_PHRASE, top + mTextHeight + DP_PHRASE);
        } else {
          border = new RectF(x - DP_ACTIVE, top - DP_ACTIVE, x + measuredText + DP_ACTIVE, top + mTextHeight + DP_ACTIVE);
        }
  
        paint.setColor(mBorderColor);
        paint.setStrokeWidth(4.0f);
        paint.setStyle(Paint.Style.STROKE);
  
        canvas.drawRoundRect(border, DP_ROUNDED, DP_ROUNDED, paint);
      }
  
      paint.setStyle(Paint.Style.FILL);
      paint.setColor(paintColor);
      canvas.drawText(text, start, end, x, y, paint);
  
      canvas.restore();
    }
  
    private float measureText(Paint paint, CharSequence text, int start, int end) {
      return paint.measureText(text, start, end);
    }
  }
  </code></pre>