Android计算文本宽度的坑

想实现自动缩放字体大小的功能,无论用第三方的控件还是AppCompatTextView都遇到一个问题:在滚动列表视图中显示有问题,在滚动过程中,明明有更大空间却用了一个小字体。没有时间细查实现,想着很简单就造个轮子。
想法很简单:计算每个非目标child的wrap_content宽度,用父宽度减去得availableWidth,再计算目标child的宽度,如果超过就减小字体值,直至合适的字体大小值出现。
然而实现后实际宽度比预期少那几个像素,使得相邻的文本折行了。

mRight是相邻文本TextView,mLeft是可绽放字体的TextView:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
long time = System.currentTimeMillis();

final int mode = MeasureSpec.getMode(widthMeasureSpec) >> 30;
final int width = MeasureSpec.getSize(widthMeasureSpec);
mRight.measure(0, 0);
int rightMinWidth = mRight.getMeasuredWidth();
MarginLayoutParams llp = (MarginLayoutParams) mLeft.getLayoutParams();
MarginLayoutParams rlp = (MarginLayoutParams) mRight.getLayoutParams();
int available = width - rightMinWidth - rlp.leftMargin - rlp.rightMargin
- llp.leftMargin - llp.rightMargin;

Paint textPaint = mLeft.getPaint();
CharSequence cs = mLeft.getText();
String text = cs == null ? null : cs.toString();
float textSize = mOriginTextSize;
textPaint.setTextSize(textSize);
for (int bounds = getTextSize(textPaint, text); bounds > available;
bounds = getTextSize(textPaint, text)) {
textSize -= 2;
textPaint.setTextSize(textSize);
}
time = System.currentTimeMillis() - time;
Logger.d(TAG, "onMeasure: mode=%d, width=%d, right=%d," +
" available=%d, textSize=%.2f, origin=%.2f, cost=%d",
mode, width, rightMinWidth, available, textSize, mOriginTextSize, time);

super.onMeasure(widthMeasureSpec, heightMeasureSpec);
}

private static int getTextSize(Paint paint, @Nullable String text) {
if (text == null) {
return 0;
}
paint.getTextBounds(text, 0, text.length(), sTextBounds);
return sTextBounds.width();
}

网上查找了下原因,竟是计算字体宽度的方法使用的不对!在getTextSize中应该是return sTextBounds.left + sTextBounds.width(); 太坑爹。
后来想到字体top和ascent, bottom和descent也是不一样的,是为记……

dark
sans