我正在尝试显示 TextView在 Android 中, View 中的文本是顶部对齐的:
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// Create container layout
FrameLayout layout = new FrameLayout(this);
// Create text label
TextView label = new TextView(this);
label.setTextSize(TypedValue.COMPLEX_UNIT_PX, 25); // 25 pixels tall
label.setGravity(Gravity.TOP + Gravity.CENTER); // Align text top-center
label.setPadding(0, 0, 0, 0); // No padding
Rect bounds = new Rect();
label.getPaint().getTextBounds("gdyl!", 0, 5, bounds); // Measure height
label.setText("good day, world! "+bounds.top+" to "+bounds.bottom);
label.setTextColor (0xFF000000); // Black text
label.setBackgroundColor(0xFF00FFFF); // Blue background
// Position text label
FrameLayout.LayoutParams layoutParams =
new FrameLayout.LayoutParams(300, 25, Gravity.LEFT + Gravity.TOP);
// also 25 pixels tall
layoutParams.setMargins(50, 50, 0, 0);
label.setLayoutParams(layoutParams);
// Compose screen
layout.addView(label);
setContentView(layout);
}
此代码输出以下图像:
注意事项:
- 蓝色框高 25 像素,与要求的一样
- 文本边界也按要求报告为 25 像素高 (6 - (-19) = 25)
- 文本不是从标签的顶部开始,而是在其上方有一些填充,忽略 setPadding()
- 这会导致文本在底部被剪裁,即使从技术上讲盒子足够高
如何让 TextView 在框的最顶部开始显示文本?
我对可能的答案有两个限制:
- 不过,我确实需要使文本保持顶部对齐,所以如果有一些技巧可以将其底部对齐或垂直居中,我就无法使用它,因为我遇到了 TextView 比它高的情况需要。
- 我有点兼容性狂,所以如果可能的话,我想坚持使用早期 Android API 中可用的调用(最好是 1,但绝对不超过 7)。
最佳答案
TextViews使用抽象类 android.text.Layout至 draw the text在 Canvas 上:
canvas.drawText(buf, start, end, x, lbaseline, paint);
垂直偏移 lbaseline
计算为行的底部减去字体的下降:
int lbottom = getLineTop(i+1);
int lbaseline = lbottom - getLineDescent(i);
getLineTop
和getLineDescent
这两个调用函数是抽象的,但是可以在BoringLayout 中找到一个简单的实现。 (看图...:),它只返回 mBottom
和 mDesc
的值。这些是在其 init 中计算的方法如下:
if (includepad) {
spacing = metrics.bottom - metrics.top;
} else {
spacing = metrics.descent - metrics.ascent;
}
if (spacingmult != 1 || spacingadd != 0) {
spacing = (int)(spacing * spacingmult + spacingadd + 0.5f);
}
mBottom = spacing;
if (includepad) {
mDesc = spacing + metrics.top;
} else {
mDesc = spacing + metrics.ascent;
}
此处,includepad
是一个 boolean 值,它指定文本是否应包含额外的填充以允许超出指定上升沿的字形。它可以由 TextView 的 setIncludeFontPadding 设置(正如@ggc 指出的那样)方法。
如果 includepad 设置为 true(默认值),则文本定位在 top
-field 给定的基线处。字体的指标。否则文本的基线取自 descent
-field .
因此,从技术上讲,这应该意味着我们需要做的就是关闭 IncludeFontPadding,但不幸的是,这会产生以下结果:
这样做的原因是字体报告其上升为 -23.2,而边界框报告的最高值为 -19。我不知道这是字体中的“错误”还是应该是这样的。不幸的是,FontMetrics 不提供任何与边界框报告的 19 相匹配的值,即使您尝试以某种方式将报告的 240dpi 屏幕分辨率与 72dpi 的字体点定义相结合,因此没有“官方”方式来解决这个问题。
但是,当然,可用信息可用于破解解决方案。有两种方法:
单独使用 IncludeFontPadding,即设置为 true:
double top = label.getPaint().getFontMetrics().top; label.setPadding(0, (int) (top - bounds.top - 0.5), 0, 0);
即设置垂直填充以补偿文本边界报告的 y 值与字体度量的最高值之间的差异。结果:
将 IncludeFontPadding 设置为 false:
double ascent = label.getPaint().getFontMetrics().ascent; label.setPadding(0, (int) (ascent - bounds.top - 0.5), 0, 0); label.setIncludeFontPadding(false);
即设置垂直填充以补偿文本边界报告的 y 值与字体度量的上升值之间的差异。结果:
请注意,将 IncludeFontPadding 设置为 false 并没有什么神奇之处。 两个版本应该都可以。它们产生不同结果的原因是当字体度量的浮点值转换为整数时舍入误差略有不同。碰巧在这种特殊情况下,将 IncludeFontPadding 设置为 false 看起来更好,但对于不同的字体或字体大小,这可能会有所不同。调整顶部填充的计算以产生与 BoringLayout 使用的计算相同的精确舍入误差可能相当容易。我还没有这样做,因为我宁愿使用“无错误”字体,但如果我有时间,我可能会稍后添加它。那么,IncludeFontPadding 是设置为 false 还是 true 应该是真正无关紧要的。
关于java - Android TextView 中真正的顶部对齐文本,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/26680079/