当前位置: 首页 > 面试题库 >

Java-如何在矩形中可视地将特定字符串(不仅仅是字体)居中

牛枫
2023-03-14
问题内容

我试图在视觉上将用户提供的任意字符串放在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;

        html" target="_blank">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();
        }
    }

}



 类似资料:
  • 我试图视觉上居中一个任意的用户提供的字符串在JPanel上。我在SO上读了几十个其他类似的问题和答案,但没有找到任何直接解决我遇到的问题的答案。 在下面的代码示例中,getWidth()和getHeight()指的是我放置文本字符串的JPanel的宽度和高度。我找到了那个文本布局。getBounds()可以很好地告诉我包围文本的边框的大小。因此,我认为通过计算文本边界矩形左下角JPanel上的x和

  • 问题内容: 在Java forString类中,有一个称为matchs的方法,该方法如何使用正则表达式检查我的字符串是否只有数字。我尝试了以下示例,但它们均使我返回了false。 问题答案: 尝试 要么 按照Java 正则表达式 ,其 含义是“一次或多次”,并且其含义是“数字”。 注意:“双反斜杠”是一个 转义序列, 用于获取单个反斜杠-因此,在Java字符串中会提供实际结果: 参考文献: Jav

  • 问题内容: 例如,我正在从文本文件中提取文本String,我需要这些词来形成数组。但是,当我执行所有这些操作时,有些单词以逗号(,)或句号(。)结尾,甚至附有括号(这完全是正常现象)。 我要做的是摆脱那些角色。我一直在尝试使用Java中的预定义String方法来做到这一点,但我还是无法解决。 问题答案: 用: replaceAll采用正则表达式。这个: …寻找每个逗号和/或句号。

  • 问题内容: 在Python中,如何检查字符串是否仅包含某些字符? 我需要检查仅包含a..z,0..9和的字符串。(句号),没有其他字符。 我可以遍历每个字符并检查字符是a..z还是0..9或。但这会很慢。 我现在不清楚如何使用正则表达式进行操作。 这个对吗?您可以提出更简单的正则表达式还是更有效的方法? 问题答案: 决赛(?) 答案,包装在函数中,带有注释的交互式会话: 注意:在此答案中还有一个比

  • 问题内容: 我在Java中有一个具有值的字符串变量: 我希望我的最终字符串包含值: 我怎样才能做到这一点?我是Java编程语言的新手。 谢谢, 问题答案: 您可以使用:

  • 问题内容: 我需要定义一个数组,其中包含所有以下特殊字符。 我正在用这个 它接受除“和\以外的所有字符 请帮助如何定义这两个。 问题答案: 并且是String类中的特殊字符 是String的开始或结尾 用于创建如新线的某些字符 标签或你的情况逃脱特殊字符,如和 因此,要使它们成为文字,您必须使用和 另一个想法是使用而不是,这样您就不必转义,并且您的字符可以写为或(因为要求转义- 应该写为- 在这里