我试图在视觉上将用户提供的任意字符串放在JPanel上。我在这里已经阅读了许多其他类似的问题和答案,但是还没有找到任何直接解决我所遇到的问题的方法。
在下面的代码示例中,getWidth()和getHeight()指的是我要放置文本字符串的JPanel的宽度和高度。我发现TextLayout.getBounds()很好地告诉了我包围文本的边界矩形的大小。因此,我认为通过计算文本边界矩形左下角的JPanel上的x和y位置,将文本矩形居中放置在JPanel矩形中相对简单:
FontRenderContext context = g2d.getFontRenderContext(); messageTextFont = new Font("Arial", Font.BOLD, fontSize); TextLayout txt = new TextLayout(messageText, messageTextFont, context); Rectangle2D bounds = txt.getBounds(); xString = (int)((getWidth() - (int)bounds.getWidth()) / 2 ); yString = (int)((getHeight()/2) + (int)(bounds.getHeight()/2)); g2d.setFont(messageTextFont); g2d.setColor(rxColor); g2d.drawString(messageText, xString, yString);
这对于全部都是大写的字符串非常适用。但是,当我开始测试包含带有小写字母且带有降序字母(例如g,p,y)的字符串时,文本不再居中。在JPanel上,小写字母(延伸到字体基线以下的部分)的下降字体绘制得太低,以致文本看起来居中。
那时,我发现(由于SO)传递给drawString()的y参数指定了绘制文本的基线,而不是下限。因此,再次在SO的帮助下,我意识到我需要根据字符串中降序长度来调整文本的位置:
.... TextLayout txt = new TextLayout(messageText, messageTextFont, context); Rectangle2D bounds = txt.getBounds(); int descent = (int)txt.getDescent(); xString = (int)((getWidth() - (int)bounds.getWidth()) / 2 ); yString = (int)((getHeight()/2) + (int)(bounds.getHeight()/2) - descent); ....
我用重用g,p和y等小写字母的字符串进行了测试,效果很好!呜呜!可是等等。啊。现在,当我只尝试使用大写字母时,JPanel上的文本太高而无法居中显示。
那就是当我发现TextLayout.getDescent()(以及我为其他类找到的所有其他getDescent()方法)返回FONT的最大下降而不是特定字符串的最大下降。因此,我的大写字符串被提高了,以解决该字符串中甚至没有出现的后代。
我是什么做的?如果我不调整drawString()的y参数以考虑到降序,则JPanel上带有降序的小写字符串在视觉上太低了。如果我确实将drawString()的y参数调整为考虑到降序,那么在JPanel上,不包含任何带有降序字符的字符串在视觉上会过高。我似乎没有任何方法可以确定基线在GIVEN字符串的文本边界矩形中的位置。因此,我无法确切确定要传递给drawString()的y。
感谢您的帮助或建议。
当我为之苦恼时TextLayout,您可以仅使用Graphics上下文的FontMetrics,例如…
文本
import java.awt.BorderLayout; import java.awt.Color; import java.awt.Dimension; import java.awt.EventQueue; import java.awt.Font; import java.awt.FontMetrics; import java.awt.Graphics; import java.awt.Graphics2D; import java.awt.font.FontRenderContext; import java.awt.font.TextLayout; import java.awt.geom.Rectangle2D; import javax.swing.JFrame; import javax.swing.JPanel; import javax.swing.UIManager; import javax.swing.UnsupportedLookAndFeelException; public class LayoutText { public static void main(String[] args) { new LayoutText(); } public LayoutText() { EventQueue.invokeLater(new Runnable() { @Override public void run() { try { UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName()); } catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) { } JFrame frame = new JFrame("Testing"); frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); frame.setLayout(new BorderLayout()); frame.add(new TestPane()); frame.pack(); frame.setLocationRelativeTo(null); frame.setVisible(true); } }); } public class TestPane extends JPanel { private String text; public TestPane() { text = "Along time ago, in a galaxy, far, far away"; } @Override public Dimension getPreferredSize() { return new Dimension(200, 200); } @Override protected void paintComponent(Graphics g) { super.paintComponent(g); Graphics2D g2d = (Graphics2D) g.create(); g2d.setColor(Color.RED); g2d.drawLine(getWidth() / 2, 0, getWidth() / 2, getHeight()); g2d.drawLine(0, getHeight() / 2, getWidth(), getHeight() / 2); Font font = new Font("Arial", Font.BOLD, 48); g2d.setFont(font); FontMetrics fm = g2d.getFontMetrics(); int x = ((getWidth() - fm.stringWidth(text)) / 2); int y = ((getHeight() - fm.getHeight()) / 2) + fm.getAscent(); g2d.setColor(Color.BLACK); g2d.drawString(text, x, y); g2d.dispose(); } } }
好吧,在大惊小怪之后…
基本上,文本渲染发生在基线处,这使y边界的位置通常显示在该点的上方,使其看起来像在y位置上方绘制了文本
为了克服这个问题,我们需要将字体的上升减去字体下降的y位置添加到该位置…
例如…
FontRenderContext context = g2d.getFontRenderContext(); Font font = new Font("Arial", Font.BOLD, 48); TextLayout txt = new TextLayout(text, font, context); Rectangle2D bounds = txt.getBounds(); int x = (int) ((getWidth() - (int) bounds.getWidth()) / 2); int y = (int) ((getHeight() - (bounds.getHeight() - txt.getDescent())) / 2); y += txt.getAscent() - txt.getDescent();
…这就是为什么我喜欢手工呈现文本的原因…
可运行的示例…
布局
import java.awt.BorderLayout; import java.awt.Color; import java.awt.Dimension; import java.awt.EventQueue; import java.awt.Font; import java.awt.FontMetrics; import java.awt.Graphics; import java.awt.Graphics2D; import java.awt.font.FontRenderContext; import java.awt.font.TextLayout; import java.awt.geom.Rectangle2D; import javax.swing.JFrame; import javax.swing.JPanel; import javax.swing.UIManager; import javax.swing.UnsupportedLookAndFeelException; public class LayoutText { public static void main(String[] args) { new LayoutText(); } public LayoutText() { EventQueue.invokeLater(new Runnable() { @Override public void run() { try { UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName()); } catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) { } JFrame frame = new JFrame("Testing"); frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); frame.setLayout(new BorderLayout()); frame.add(new TestPane()); frame.pack(); frame.setLocationRelativeTo(null); frame.setVisible(true); } }); } public class TestPane extends JPanel { private String text; public TestPane() { text = "Along time ago, in a galaxy, far, far away"; } @Override public Dimension getPreferredSize() { return new Dimension(200, 200); } @Override protected void paintComponent(Graphics g) { super.paintComponent(g); Graphics2D g2d = (Graphics2D) g.create(); g2d.setColor(Color.RED); g2d.drawLine(getWidth() / 2, 0, getWidth() / 2, getHeight()); g2d.drawLine(0, getHeight() / 2, getWidth(), getHeight() / 2); FontRenderContext context = g2d.getFontRenderContext(); Font font = new Font("Arial", Font.BOLD, 48); TextLayout txt = new TextLayout(text, font, context); Rectangle2D bounds = txt.getBounds(); int x = (int) ((getWidth() - (int) bounds.getWidth()) / 2); int y = (int) ((getHeight() - (bounds.getHeight() - txt.getDescent())) / 2); y += txt.getAscent() - txt.getDescent(); g2d.setFont(font); g2d.setColor(Color.BLACK); g2d.drawString(text, x, y); g2d.setColor(Color.BLUE); g2d.translate(x, y); g2d.draw(bounds); g2d.dispose(); } } }
查看使用文本API以获得更多信息…
更新
正如已经建议的,您可以使用GlyphVector…
每个单词(Cat和Dog)分开计算以证明差异
猫狗
import java.awt.BorderLayout; import java.awt.Color; import java.awt.Dimension; import java.awt.EventQueue; import java.awt.Font; import java.awt.Graphics; import java.awt.Graphics2D; import java.awt.font.FontRenderContext; import java.awt.font.GlyphVector; import java.awt.geom.Rectangle2D; import javax.swing.JFrame; import javax.swing.JPanel; import javax.swing.UIManager; import javax.swing.UnsupportedLookAndFeelException; public class LayoutText { public static void main(String[] args) { new LayoutText(); } public LayoutText() { EventQueue.invokeLater(new Runnable() { @Override public void run() { try { UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName()); } catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) { } JFrame frame = new JFrame("Testing"); frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); frame.setLayout(new BorderLayout()); frame.add(new TestPane()); frame.pack(); frame.setLocationRelativeTo(null); frame.setVisible(true); } }); } public class TestPane extends JPanel { private String text; public TestPane() { text = "A long time ago, in a galaxy, far, far away"; } @Override public Dimension getPreferredSize() { return new Dimension(200, 200); } @Override protected void paintComponent(Graphics g) { super.paintComponent(g); Graphics2D g2d = (Graphics2D) g.create(); g2d.setColor(Color.RED); g2d.drawLine(getWidth() / 2, 0, getWidth() / 2, getHeight()); g2d.drawLine(0, getHeight() / 2, getWidth(), getHeight() / 2); Font font = new Font("Arial", Font.BOLD, 48); g2d.setFont(font); FontRenderContext frc = g2d.getFontRenderContext(); GlyphVector gv = font.createGlyphVector(frc, "Cat"); Rectangle2D box = gv.getVisualBounds(); int x = 0; int y = (int)(((getHeight() - box.getHeight()) / 2d) + (-box.getY())); g2d.drawString("Cat", x, y); x += box.getWidth(); gv = font.createGlyphVector(frc, "Dog"); box = gv.getVisualBounds(); y = (int)(((getHeight() - box.getHeight()) / 2d) + (-box.getY())); g2d.drawString("Dog", x, y); g2d.dispose(); } } }