如何在Java中绘制垂直居中的字符串?

21

我知道这是一个简单的概念,但我在字体度量方面遇到了困难。水平居中并不太难,但垂直居中似乎有些困难。

我尝试过使用FontMetrics getAscent、getLeading、getXXXX方法以各种组合方式,但无论我尝试什么,文本始终会偏离几个像素。有没有一种方法可以测量文本的确切高度,以便它完全居中。

4个回答

52

注意,你需要确切考虑垂直居中的含义。确实需要这样做。

字体是以基线为准进行渲染的,基线位于文本底部。垂直空间的分配如下:

---
 ^
 |  leading
 |
 -- 
 ^              Y     Y
 |               Y   Y
 |                Y Y
 |  ascent         Y     y     y 
 |                 Y      y   y
 |                 Y       y y
 -- baseline ______Y________y_________
 |                         y                
 v  descent              yy
 --

行距指的是字体推荐的行间距。为了在两个点之间垂直居中,您应该忽略行距(顺便说一下,它是“leading”,而不是“leeding”。在印刷版中,行距通常指印刷版中插入行之间的导线间距)。

因此,为了使文本上升部分和下降部分居中,您需要选择一个基于 x-height 的值作为样式的行高,而不是使用具体的行距值。

baseline=(top+((bottom+1-top)/2) - ((ascent + descent)/2) + ascent;

没有添加“+ ascent”时,您得到的是字体顶部的位置;因此添加升高(ascent)是从顶部到基线的过程。

此外,请注意字体高度应该包括行间距(leading),但某些字体不包括它,由于四舍五入的差异,字体高度可能并不完全等于(leading + ascent + descent)。


哦,太棒了,那是一个完美的解释。非常感谢你!(还有关于 leading 的解释,我之前不知道) - brimborium
1
为什么要加1呢? - Max
1
@Alex:因为(bottom+1-top)是高度;如果底部和顶部相同,则高度为1,而不是0。其净效应是在中点为n.5像素的情况下向下舍入。 - Lawrence Dol

11

我在这里找到了一份配方

关键的方法似乎是getStringBounds()getAscent()

// Find the size of string s in font f in the current Graphics context g.
FontMetrics fm   = g.getFontMetrics(f);
java.awt.geom.Rectangle2D rect = fm.getStringBounds(s, g);

int textHeight = (int)(rect.getHeight()); 
int textWidth  = (int)(rect.getWidth());
int panelHeight= this.getHeight();
int panelWidth = this.getWidth();

// Center text horizontally and vertically
int x = (panelWidth  - textWidth)  / 2;
int y = (panelHeight - textHeight) / 2  + fm.getAscent();

g.drawString(s, x, y);  // Draw the string.

(注意:上述代码受到页面注明的MIT许可证的保护。)


2
是的...我熟悉这个概念...但是它是错误的。fm.getAscent() 方法是问题所在。它不报告字体的实际像素上升值,导致文本更接近底部而不是顶部。 - Paul Alexander

1
另一个选项是使用TextLayout类中的getBounds方法。
Font f;
// code to create f
String TITLE = "Text to center in a panel.";
FontRenderContext context = g2.getFontRenderContext();

TextLayout txt = new TextLayout(TITLE, f, context);
Rectangle2D bounds = txt.getBounds();
int xString = (int) ((getWidth() - bounds.getWidth()) / 2.0 );
int yString = (int) ((getHeight() + bounds.getHeight()) / 2.0);
// g2 is the graphics object 
g2.setFont(f);
g2.drawString(TITLE, xString, yString);

1

不确定这是否有帮助,但是drawString(s, x, y)将文本的基线设置为y。

我正在尝试进行一些垂直居中的工作,但在注意到文档中提到的行为之前,无法使文本看起来正确。我以为字体底部在y处。

对我来说,解决方法是从y坐标中减去fm.getDescent()


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接