现象
原生的TextView 在内容是中英文数字混合的情况下,换行后会有留白,像这样:
可以看到文本右侧的空白,原因是TextView换行后依然使用了原内容的最大宽度(我设置了TextView的maxWidth)
一种简单优化办法
既然问题是宽度引起的,那么在onMeasure里手动计算下TextView的最大宽度。
//自定义View继承自AppCompatTextView
public void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
Layout layout = getLayout();
if (layout == null || layout.getLineCount() <= 1) {
// 只有一行无需处理
return;
}
// 最大宽度
int maxWidth = 0;
int totalHeight = 0;
for (int i = layout.getLineCount() - 1; i >= 0; --i) {
int lineStart = layout.getLineStart(i);
int lineEnd = layout.getLineEnd(i);
// 获取每行文字的宽度
float lineWidth = layout.getPaint().measureText(getText(), lineStart, lineEnd);
// 获取TextView最大宽度
maxWidth = Math.max(maxWidth, Math.round(lineWidth));
// 获取每行的高度和行间距
totalHeight += layout.getLineBottom(i) - layout.getLineTop(i);
}
// 避免宽度超出父视图的限制
int desiredWidth = Math.min(maxWidth + getPaddingLeft() + getPaddingRight(), MeasureSpec.getSize(widthMeasureSpec));
int desiredHeight = totalHeight + getPaddingTop() + getPaddingBottom();
// 设置测量结果 明确告诉系统这个视图的宽度和高度是多少。系统会根据这些尺寸进行布局。
setMeasuredDimension(desiredWidth, desiredHeight);
}
显示如下
![]() |
![]() |
特殊情况,当文本有样式SpannableString的时候,宽度会有问题:

其原因是有样式时,还需要加上左边缘样式的宽度,否则会导致宽度测量不准确
// 获取行内缩进
int paragraphLeft = getLayout().getParagraphLeft(i);
// 获取每行文字的宽度
float lineWidth = layout.getPaint().measureText(getText(), lineStart, lineEnd) + paragraphLeft;

最后附上这种简单优化的代码
/**
* @author r3562939
* @date 2024/12/5
*/
public class SimpleLayoutTextView extends AppCompatTextView {
public SimpleLayoutTextView(Context context) {
super(context);
}
public SimpleLayoutTextView(Context context, AttributeSet attrs) {
super(context, attrs);
}
public SimpleLayoutTextView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
public void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
Layout layout = getLayout();
if (layout == null || layout.getLineCount() <= 1) {
// 只有一行无需处理
return;
}
int maxWidth = 0;
int totalHeight = 0;
// 输出最大宽度
for (int i = layout.getLineCount() - 1; i >= 0; --i) {
int lineStart = layout.getLineStart(i);
int lineEnd = layout.getLineEnd(i);
// 获取行内缩进
int paragraphLeft = getLayout().getParagraphLeft(i);
// 获取每行文字的宽度
float lineWidth = layout.getPaint().measureText(getText(), lineStart, lineEnd) + paragraphLeft;
// 获取TextView最大宽度
maxWidth = Math.max(maxWidth, Math.round(lineWidth));
// 获取每行的高度和行间距
totalHeight += layout.getLineBottom(i) - layout.getLineTop(i);
}
// 避免宽度超出父视图的限制
int desiredWidth = Math.min(maxWidth + getPaddingLeft() + getPaddingRight(), MeasureSpec.getSize(widthMeasureSpec));
int desiredHeight = totalHeight + getPaddingTop() + getPaddingBottom();
// 设置测量结果 明确告诉系统这个视图的宽度和高度是多少。系统会根据这些尺寸进行布局。
setMeasuredDimension(desiredWidth, desiredHeight);
}
}