EDIT: I have an updated solution. Sometimes the drawBackground method in a solution with LineBackgroundSpan was simply not called without any reason. The new version works all the time and looks much clearer.
I met the same problem and I solved it by combining these solutions:
The result code is as follows:
private static class DottedUnderlineSpan extends ReplacementSpan { private Paint p; private int mWidth; private String mSpan; private float mSpanLength; private boolean mLengthIsCached = false; public DottedUnderlineSpan(int _color, String _spannedText){ p = new Paint(); p.setColor(_color); p.setStyle(Paint.Style.STROKE); p.setPathEffect(new DashPathEffect(new float[]{mDashPathEffect, mDashPathEffect}, 0)); p.setStrokeWidth(mStrokeWidth); mSpan = _spannedText; } @Override public int getSize(Paint paint, CharSequence text, int start, int end, Paint.FontMetricsInt fm) { mWidth = (int) paint.measureText(text, start, end); return mWidth; } @Override public void draw(Canvas canvas, CharSequence text, int start, int end, float x, int top, int y, int bottom, Paint paint) { canvas.drawText(text, start, end, x, y, paint); if(!mLengthIsCached) mSpanLength = paint.measureText(mSpan);
To make your underline look the same at all densities, set the dimensions in dp
mStrokeWidth = context.getResources().getDimension(R.dimen.stroke_width); mDashPathEffect = context.getResources().getDimension(R.dimen.dash_path_effect); mOffsetY = context.getResources().getDimension(R.dimen.offset_y);
And here is how you use it:
DottedUnderlineSpan dottedUnderlineSpan = new DottedUnderlineSpan(0xFF00FF00, spannedText); spannableString.setSpan(dottedUnderlineSpan, startOfText, endOfText, Spanned.SPAN_INCLUSIVE_INCLUSIVE);
And make sure you turn off hardware acceleration in textView , which will show the underline interval. fooobar.com/questions/36881 / ...
EDIT: if you see ways to improve this solution, I would appreciate any good points.