【问题标题】:Swing drawString: Text bounds and line wrappingSwing drawString:文本边界和换行
【发布时间】:2017-04-27 23:15:45
【问题描述】:

我需要用Graphics#drawString来绘制文字

我在宽度和高度都发生变化(通过拖动)的 JPanel 上绘图。

我正在寻找一种生成边界的解决方案,以便我可以自动扭曲线条并相应地调整文本,而不会溢出。

我想我可以通过使用 fontMetrics 获取以像素为单位的长度来自己对其进行硬编码,但是,我宁愿有一个自动执行此操作的组件(drawString 也不支持 '\n')。

在文档以及this 其他答案中,我发现了这个:

Graphics2D g = ...;
Point2D loc = ...;
Font font = Font.getFont("Helvetica-bold-italic");
FontRenderContext frc = g.getFontRenderContext();
TextLayout layout = new TextLayout("This is a string", font, frc);
layout.draw(g, (float)loc.getX(), (float)loc.getY());

Rectangle2D bounds = layout.getBounds();
bounds.setRect(bounds.getX()+loc.getX(),
              bounds.getY()+loc.getY(),
              bounds.getWidth(),
              bounds.getHeight());
g.draw(bounds);

它确实绘制了字符串和边界,但它们没有效果,所以这里没有运气。

我可以使用任何类吗?

【问题讨论】:

    标签: java swing layout graphics


    【解决方案1】:

    我可以使用任何类吗?

    另一种选择是使用LineBreakMeasurer

    import java.awt.*;
    import java.awt.font.*;
    import java.awt.geom.*;
    import java.text.*;
    import java.util.Objects;
    import javax.swing.*;
    import javax.swing.border.Border;
    
    public final class LineBreakMeasurerTest {
      private static final String TEXT = "1234567890 ABCDEFG HIJKLMN OPQRSTU VWXYZ";
      private final JLabel    lbl1 = new JLabel(TEXT);
      private final JTextArea lbl2 = new JTextArea(TEXT);
      private final JLabel    lbl3 = new WrappingLabel(TEXT);
      public JComponent makeUI() {
        Border b = BorderFactory.createLineBorder(Color.GREEN,  5);
        lbl1.setBorder(
            BorderFactory.createTitledBorder(b, "JLabel"));
        lbl2.setBorder(
            BorderFactory.createTitledBorder(b, "JTextArea"));
        lbl3.setBorder(
            BorderFactory.createTitledBorder(b, "LineBreakMeasurer"));
    
        lbl2.setFont(lbl1.getFont());
        lbl2.setEditable(false);
        lbl2.setLineWrap(true);
        lbl2.setWrapStyleWord(true);
        lbl2.setBackground(lbl1.getBackground());
    
        JPanel p = new JPanel(new GridLayout(3, 1));
        p.add(lbl1);
        p.add(lbl2);
        p.add(lbl3);
        return p;
      }
    
      public static void main(String... args) {
        EventQueue.invokeLater(() -> {
          UIManager.put("swing.boldMetal", Boolean.FALSE);
          JFrame f = new JFrame();
          f.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
          f.getContentPane().add(new LineBreakMeasurerTest().makeUI());
          f.setSize(320, 240);
          f.setLocationRelativeTo(null);
          f.setVisible(true);
        });
      }
    }
    
    class WrappingLabel extends JLabel {
      //TEST: private AffineTransform at = AffineTransform.getScaleInstance(.8, 1d);
      protected WrappingLabel(String text) {
        super(text);
      }
      @Override protected void paintComponent(Graphics g) {
        Graphics2D g2 = (Graphics2D) g.create();
        g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
                            RenderingHints.VALUE_ANTIALIAS_ON);
        g2.setPaint(getForeground());
        Insets i = getInsets();
        float x = i.left;
        float y = i.top;
        int w = getWidth() - i.left - i.right;
    
        AttributedString as = new AttributedString(getText());
        //TEST: as.addAttribute(TextAttribute.FONT, g2.getFont());
        //TEST: as.addAttribute(TextAttribute.TRANSFORM, at);
        AttributedCharacterIterator aci = as.getIterator();
        FontRenderContext frc = g2.getFontRenderContext();
        LineBreakMeasurer lbm = new LineBreakMeasurer(aci, frc);
    
        while (lbm.getPosition() < aci.getEndIndex()) {
          TextLayout tl = lbm.nextLayout(w);
          tl.draw(g2, x, y + tl.getAscent());
          y += tl.getDescent() + tl.getLeading() + tl.getAscent();
        }
        g2.dispose();
      }
    }
    

    【讨论】:

    • 如果你想让文本居中,你可以用这个代替xfloat xPos = (float) (x + ((getWidth() - tl.getBounds().getWidth()) / 2))
    【解决方案2】:

    最简单的解决方案:使用 JTextArea。

    您可以通过将背景设置为 null、使其不可聚焦来装配它,使其看起来甚至不像 JTextArea,如果您在其上调用 setLineWrap(true)setWrapStyleWord(true),它会自动换行。要让它填充 JPanel,给面板一个 BorderLayout,然后添加 JTextArea BorderLayout.CENTER。如果需要,您甚至可以设置边距。

    【讨论】:

    • 感谢您的回答。我坚持了这一点,但没有别的办法吗?我这样做会遇到很多麻烦,因为它不是一个统一的文本。这是很多小字符串,没有对齐,具有不同的格式、颜色、规格。这是一个非常简化的模式,其中包含一些:imgur.com/4zKTppM 红色表格是虚构的,仅用于显示对齐方式。字符串独立更新,这个面板非常小,而且数量很多..
    猜你喜欢
    • 2012-03-17
    • 2011-05-04
    • 2011-12-08
    • 1970-01-01
    • 2013-09-01
    • 1970-01-01
    • 2013-12-25
    • 1970-01-01
    • 2014-10-17
    相关资源
    最近更新 更多