Android设置lineSpacing小于1时BackgroundColorSpan显示异常如何解决
问题场景
Android开发中给TextView设置了自定义行间距:
textView.setLineSpacing(1f, .70f);
后续需要给文本中指定词汇设置背景色,使用原生BackgroundColorSpan实现:
spannableStringBuilder.setSpan(new BackgroundColorSpan(bckgndColor), spanStart, spanEnd, 0);
设置后文本背景出现断裂、错位问题:
注释掉行间距设置代码后,显示效果完全正常:
查看BackgroundColorSpan源码发现,其仅在updateDrawState中修改了TextPaint的背景色属性:
/** * Updates the background color of the TextPaint. */ @Override public void updateDrawState(@NonNull TextPaint textPaint) { textPaint.bgColor = mColor; }
即使继承BackgroundColorSpan重写方法,也无法拿到绘制区域的Rect对象调整背景范围,需要可行的修复方案。
解决方案
问题根源
原生BackgroundColorSpan的背景绘制逻辑完全依赖TextPaint的默认行度量值,当调用setLineSpacing设置额外行间距、行高倍数时,系统会在默认文本行之外追加额外的布局间距,原生Span不会感知这部分间距,最终导致换行处背景断裂、位置错位。
不要使用原生BackgroundColorSpan,自定义实现LineBackgroundSpan接口即可解决问题:该接口的绘制回调会直接返回系统计算好的、包含所有自定义行间距的实际行绘制边界,你可以完全控制背景的绘制范围。
实现代码
public class CustomBackgroundColorSpan implements LineBackgroundSpan { private final int mBgColor; public CustomBackgroundColorSpan(int bgColor) { mBgColor = bgColor; } @Override public void drawBackground(@NonNull Canvas canvas, @NonNull Paint paint, int lineLeft, int lineRight, int lineTop, int baseline, int lineBottom, @NonNull CharSequence text, int lineStart, int lineEnd, int lineNum) { Spannable spannableText = (Spannable) text; int spanStart = spannableText.getSpanStart(this); int spanEnd = spannableText.getSpanEnd(this); // 当前行不在Span覆盖范围内,直接跳过 if (spanStart >= lineEnd || spanEnd <= lineStart) { return; } // 计算当前行内Span实际需要绘制的左右边界,避免跨行时整行被背景填充 int drawLeft = lineLeft; int drawRight = lineRight; TextPaint measurePaint = new TextPaint(paint); if (lineStart < spanStart) { drawLeft += (int) measurePaint.measureText(text, lineStart, spanStart); } if (lineEnd > spanEnd) { drawRight = lineLeft + (int) measurePaint.measureText(text, lineStart, spanEnd); } // 绘制背景 int originPaintColor = paint.getColor(); paint.setColor(mBgColor); canvas.drawRect(drawLeft, lineTop, drawRight, lineBottom, paint); paint.setColor(originPaintColor); } }
使用说明
- 替换原有
BackgroundColorSpan,传入上述自定义Span实例即可,设置Span时flag推荐使用Spannable.SPAN_EXCLUSIVE_EXCLUSIVE,不要传0,避免后续文本变动时Span范围异常。 - 该实现自动适配任意行间距、行高倍数设置,不会出现背景断裂错位问题。如果需要圆角背景,只需要把
drawRect替换为drawRoundRect,传入自定义圆角半径即可,拓展性远高于原生实现。
内容的提问来源于stack exchange,提问作者AndreiBogdan
相关产品推荐
相关产品推荐

