The @bpronin code didn’t work for me - the range was too small on the high-resolution screen, and it covered all the text, and not only covered the error.
But after his idea, I updated my answer to remove the need to add a resource:
public class ErrorSpan extends DynamicDrawableSpan { private int width; int lineWidth; int waveSize; int color; public ErrorSpan(Resources resources) { this(resources, Color.RED, 1, 3); } public ErrorSpan(Resources resources, int color, int lineWidth, int waveSize) { super(DynamicDrawableSpan.ALIGN_BASELINE); // Get the screen density scale final float scale = resources.getDisplayMetrics().density; // Convert the dps to pixels, based on density scale this.lineWidth = (int) (lineWidth * scale + 0.5f); this.waveSize = (int) (waveSize * scale + 0.5f); this.color = color; } @Override public Drawable getDrawable() { return null; } @Override public int getSize(Paint paint, CharSequence text, int start, int end, Paint.FontMetricsInt fm) { width = (int) paint.measureText(text, start, end); return width; } @Override public void draw(Canvas canvas, CharSequence text, int start, int end, float x, int top, int y, int bottom, Paint paint) { Paint p = new Paint(paint); p.setColor(color); p.setStrokeWidth(lineWidth); int doubleWaveSize = waveSize * 2; for (int i = (int)x; i < x + width; i += doubleWaveSize) { canvas.drawLine(i, bottom, i + waveSize, bottom - waveSize, p); canvas.drawLine(i + waveSize, bottom - waveSize, i + doubleWaveSize, bottom, p); } canvas.drawText(text.subSequence(start, end).toString(), x, y, paint); } }
source share