Search code examples
androidspannablestring

How to add red wavy line below text in Android's TextView


I'm trying to add red wavy line below errors in texts, such as:

enter image description here

Unfortunately I can't find a proper *Span class to wrap the error text with.

How should I implement such a feature in Android?


Solution

  • @bpronin's code did not work for me - the span was too small on high resolution screen and it spanned the entire text, not only the error spanned.

    But following his idea I updated my answer to remove the need for added 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's 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);
        }
    }