【问题标题】:Calculating lines with LineBreakMeasurer and change fontsize使用 LineBreakMeasurer 计算线条并更改字体大小
【发布时间】:2013-03-20 17:29:25
【问题描述】:

我已经使用其他出色的答案来设计一种方法,该方法使用LineBreakMeasurer 返回JTextPane 的正确尺寸,给定它的内容和宽度。

问题在于,当我尝试不同的字体大小时,它会有点问题,我不知道为什么。它非常适用于 Arial 12,但不适用于 Arial 8 或 Arial 16,因为 nextLayout 计算长度错误。谁能告诉我为什么?

import java.awt.Color;
import java.awt.Dimension;
import java.awt.FlowLayout;
import java.awt.Font;
import java.awt.FontMetrics;
import java.awt.font.FontRenderContext;
import java.awt.font.LineBreakMeasurer;
import java.awt.font.TextLayout;
import java.text.AttributedCharacterIterator;
import java.text.AttributedString;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JTextPane;
import javax.swing.border.LineBorder;


public class Example1 {

static JFrame frame = new JFrame();
static JPanel bigPanel = new JPanel();
static JTextPane textPane[] = new JTextPane[10];



public static void main(String[] args) {

    frame.setLayout(new FlowLayout());
    frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);

    for(int i=0 ; i<10 ; i++) {
        textPane[i] = new JTextPane();
        textPane[i].setFont(new Font("Arial", 0, 12+i ));
        textPane[i].setText("This is 10 characters of " + ((Integer)(10+i)).toString() + " points : a b c d e f g h i j");
        textPane[i].setPreferredSize(getTextDimension(textPane[i], 100));
        textPane[i].setBorder(new LineBorder(Color.BLACK));
        frame.add(textPane[i]);
    }

    frame.setSize(500, 500);
    frame.setVisible(true);

}

static private Dimension getTextDimension(JTextPane textPane, float width) {

    FontMetrics fm = textPane.getFontMetrics(textPane.getFont());
    int fontHeight = fm.getHeight()+ fm.getDescent();
    //int fontHeight = fm.getHeight();

    AttributedString text = new AttributedString(textPane.getText());
    FontRenderContext frc = textPane.getFontMetrics(textPane.getFont()).getFontRenderContext();
    AttributedCharacterIterator styledText = text.getIterator();
    LineBreakMeasurer measurer = new LineBreakMeasurer(styledText, frc);

    float wrappingWidth = width - textPane.getInsets().left - textPane.getInsets().right;

    int lines = 0;
     while (measurer.getPosition() < styledText.getEndIndex()) {
        TextLayout layout = measurer.nextLayout(wrappingWidth);
        lines++;
     }

     return new Dimension((int)Math.round(width), lines * fontHeight);
}

}

【问题讨论】:

  • 请引用相关答案并编辑您的问题以包含一个 sscce 来展示您所描述的问题。
  • 我已将示例编辑为 sscce,这表明当字体大小增加时计算会略微偏离(即使包括字体上升和下降)
  • +1 表示sscce。也可以考虑WindowAdaptersetDefaultCloseOperation()
  • 更改示例以使用setDefaultCloseOperation()(只有更好的代码,与实际问题无关)。一直在学习,感谢@thrashgod

标签: java swing fonts awt jtextpane


【解决方案1】:

我通过在 AttributedString 实例化后添加以下行来修复字体大小问题:

AttributedString text = new AttributedString(textPane.getText());
text.addAttribute(TextAttribute.FONT, textPane.getFont());

我猜 Attributed String 需要定义字体,虽然我认为它是由 FontRenderContext 处理的,但至少对我来说不是。

【讨论】:

  • 太棒了,成功了!我几乎放弃了这个,谢谢。
【解决方案2】:

我不确定您的getTextDimension() 有什么问题,但更可靠的方法是让文本组件进行计算。 How can I measure/calculate the size a Document needs to render itself?提供了几种观点

附录:这是@camickr 的TextPanePerfectSize 的变体。

import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.GridLayout;
import java.awt.Rectangle;
import java.awt.event.ActionEvent;
import javax.swing.AbstractAction;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTextPane;
import javax.swing.border.LineBorder;
import javax.swing.text.BadLocationException;
import javax.swing.text.SimpleAttributeSet;
import javax.swing.text.StyleConstants;
import javax.swing.text.StyledDocument;

/**
 * @see https://stackoverflow.com/a/15983197/230513
 * @see https://stackoverflow.com/a/3318949/230513
 */
public class Test {

    private static final int SIZE = 16;
    private static int size = SIZE;
    private static JTextPane textPane[] = new JTextPane[SIZE];

    public static void main(String[] args) {
        final JFrame frame = new JFrame();
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        JPanel buttonPanel = new JPanel();
        buttonPanel.add(new JButton(new AbstractAction("Update") {
            @Override
            public void actionPerformed(ActionEvent e) {
                for (JTextPane jtp : textPane) {
                    StyledDocument doc = (StyledDocument) jtp.getDocument();
                    SimpleAttributeSet style = new SimpleAttributeSet();
                    StyleConstants.setFontFamily(style, "Serif");
                    StyleConstants.setFontSize(style, size++);
                    try {
                        doc.insertString(doc.getLength(), " one two three", style);
                        Dimension d = jtp.getPreferredSize();
                        Rectangle r = jtp.modelToView(jtp.getDocument().getLength());
                        d.height = r.y + r.height;
                        jtp.setPreferredSize(d);
                    } catch (BadLocationException ex) {
                        ex.printStackTrace(System.err);
                    }
                    frame.pack();
                }
            }
        }));
        frame.add(buttonPanel, BorderLayout.NORTH);
        JPanel textPanel = new JPanel(new GridLayout(0, 4));
        for (int i = 0; i < textPane.length; i++) {
            textPane[i] = new JTextPane();
            textPane[i].setText("Text pane number " + String.valueOf(i + 1));
            textPane[i].setBorder(new LineBorder(Color.BLACK));
            textPanel.add(textPane[i]);
        }
        frame.add(new JScrollPane(textPanel));
        frame.pack();
        frame.setVisible(true);
    }
}

【讨论】:

  • 注意:setPreferredSize() 仅用于演示。
  • 谢谢,但我认为这不能解决我的问题。 getTextDimension() 试图做的是计算给定宽度的文本的高度,所以基本上我的问题是我想使用 setPrefferedSize() 但只设置宽度而不是高度,让文本窗格计算出来本身
  • 示例依赖初始宽度;窗格提供了新的高度。
  • 是的,但是依赖于布局管理器来限制它的宽度。我需要将文本窗格设置为在没有布局管理器的情况下工作(取决于用户是否将其拖动到正确的位置)。如果我更改您的示例(顺便说一下非常聪明)以使用frame.setLayout(new FlowLayout()); 而不是GridLayout,然后在jtp.setPreferredSize(new Dimension(80,500));try 之后使用jtp.setPreferredSize(new Dimension(80,500)); 在计算之前强制jtp 的宽度,它几乎可以工作但后来我搞砸了而是换行。
  • JInternalFrame 可能是拖动JTextPane 的更简单方法。
猜你喜欢
  • 2014-12-06
  • 1970-01-01
  • 1970-01-01
  • 2019-11-13
  • 1970-01-01
  • 2017-04-03
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多