Swing drawString: граници на текст и обвиване на редове

Трябва да начертая текст с Graphics#drawString

Рисувам върху JPanel, който се променя както на ширина, така и на височина (чрез плъзгане).

Търся решение за генериране на граници, така че да мога автоматично да деформирам редовете и да адаптирам текста съответно, без преливания.

Реших, че мога просто да го кодирам сам, като получа дължината в пиксели с fontMetrics, но бих предпочел компонент, който прави това автоматично (drawString също не поддържа '\n').

В документите, както и в този друг отговор намерих това:

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

Което рисува низа и границите, но те нямат ефект, така че няма късмет тук.

Всеки клас, който мога да използвам?


person Lory A    schedule 13.12.2016    source източник


Отговори (2)


Някакъв клас, който мога да използвам?

Друг вариант е да използвате 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();
  }
}
person aterai    schedule 13.12.2016
comment
Ако искате текстът да е центриран, можете да използвате това вместо x, float xPos = (float) (x + ((getWidth() - tl.getBounds().getWidth()) / 2)) - person Mr. Polywhirl; 08.05.2019

Най-простото решение: използвайте JTextArea.

Можете да го монтирате така, че дори да не изглежда като JTextArea, като зададете background на null, като го направите нефокусиран и той автоматично ще обвива думите, ако извикате setLineWrap(true) и setWrapStyleWord(true) върху него. За да запълни JPanel, дайте на панела BorderLayout и добавете JTextArea BorderLayout.CENTER. Можете дори да зададете марж, ако желаете.

person Hovercraft Full Of Eels    schedule 13.12.2016
comment
Благодаря за отговора. За това се сетих, но няма ли друг начин? Ще имам много проблеми с това, тъй като това не е един общ текст. Това е много малки струни, не подравнени, с различен формат, цвят, спецификации. Ето много опростена схема с няколко: imgur.com/4zKTppM Червената таблица е въображаема и нарисувана само за показване подравняванията. Низовете се актуализират независимо, този панел е МНОГО малък и има доста от тях.. - person Lory A; 13.12.2016