当前位置: 首页 > 知识库问答 >
问题:

Java-如何在视觉上将特定字符串(不仅仅是字体)放在矩形中

陆才俊
2023-03-14

我试图视觉上居中一个任意的用户提供的字符串在JPanel上。我在SO上读了几十个其他类似的问题和答案,但没有找到任何直接解决我遇到的问题的答案。

在下面的代码示例中,getWidth()和getHeight()指的是我放置文本字符串的JPanel的宽度和高度。我找到了那个文本布局。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),传递给draString()的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上的位置太高,无法居中显示。

就在那时我发现了文本布局。getDescent()(以及我为其他类找到的所有其他getDescent()方法)返回字体的最大下降,而不是特定字符串的最大下降。因此,我的大写字符串被提升,以解释该字符串中甚至没有出现的派生词。

我该怎么办?如果我不调整drawString()的y参数来考虑下行,那么在JPanel上带有下行的小写字符串在视觉上就太低了。如果我调整drawString()的y参数以考虑下行字符,那么在JPanel上不包含任何带有下行字符的字符串在视觉上会过高。我似乎无法确定基线在给定字符串的文本边框中的位置。因此,我无法准确地确定要传递给drawString()的y是什么。

谢谢你的帮助和建议。

共有2个答案

莘羽
2023-03-14

我认为这个答案是正确的方法,但是我在过去有过自定义字体和获得它们的界限的问题。在一个项目中,我不得不求助于实际获取字体的轮廓并使用这些边界。这种方法可能更消耗内存,但它似乎是获得字体边界的可靠方法。

@Override
protected void paintComponent(Graphics g) {
    super.paintComponent(g);
    Font font = new Font("Arial", Font.BOLD, 48);
    String text = "Along time ago, in a galaxy, far, far away";

    Shape outline = font.createGlyphVector(g.getFontMetrics().getFontRenderContext(), text).getOutline();
    // the shape returned is located at the left side of the baseline, this means we need to re-align it to the top left corner. We also want to set it the the center of the screen while we are there
    AffineTransform transform = AffineTransform.getTranslateInstance(
                -outline.getBounds().getX() + getWidth()/2 - outline.getBounds().width / 2, 
                -outline.getBounds().getY() + getHeight()/2 - outline.getBounds().height / 2);
    outline = transform.createTransformedShape(outline);
    g2d.fill(outline);
}

正如我之前所说的,尝试使用字体度量,但如果所有其他方法都失败,请尝试此方法。

娄建义
2023-03-14

当我在使用TextLayout时,您可以使用图形上下文的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...

更新

如前所述,您可以使用字形向量。。。

分别计算每个单词(CatDog)以显示差异

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();
        }
    }

}
 类似资料:
  • 问题内容: 我试图在视觉上将用户提供的任意字符串放在JPanel上。我在这里已经阅读了许多其他类似的问题和答案,但是还没有找到任何直接解决我所遇到的问题的方法。 在下面的代码示例中,getWidth()和getHeight()指的是我要放置文本字符串的JPanel的宽度和高度。我发现TextLayout.getBounds()很好地告诉了我包围文本的边界矩形的大小。因此,我认为通过计算文本边界矩形

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

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

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

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

  • 问题内容: 我有一串琴弦,其中一些琴弦。我只想删除最后四个字符。 换句话说,我有 我希望它成为 Python处理此问题的方法是什么? 问题答案: