【问题标题】:Java JTextPane does not update its size when Character Attributes change当字符属性更改时,Java JTextPane 不会更新其大小
【发布时间】:2021-04-22 16:09:13
【问题描述】:

我正在用 Java 8 编写一个基于 Swing 的 UI 组件,它基本上在一个可垂直滚动的字段中显示多个 JTextPanes,并有一个用于基本样式操作(字体、字体大小、粗体等)的工具栏

为此,我使用了JPanel,它还在JScrollPane 内实现了可滚动,其中包含使用垂直BoxLayout 的多个JTextPanes。

当我开始这个时,它看起来很好,并且所有 TextPanes 都足够高以容纳一行文本并在键入更多内容时进行缩放。当我更改字体大小时,就会出现问题。当字体大小改变时,JTextPane 应该会自动调整大小以适应新文本,但是当我使用按钮来改变它时,只有当我点击两次时大小才会改变。

这里有一些截图来解释我的意思:
这是在示例开始之后,我在顶部JTextPane 添加了一些文本。这里有 2 个 TextPanes。

这是在第一次单击按钮之后,它将字体大小设置为 30pt。文字较大,但 JTextPane 不会调整大小以适应它

这是在我再次单击该按钮之后。 JTextPane 现在可以正确缩放

我已经尝试在JTextPane 及其父对象上手动调用invalidaterevalidaterepaint,但这没有任何效果。

JTextPane 也会在另一个属性更改(例如粗体)或 FontSize 更改为另一个值时调整大小。但它似乎总是落后于一个变化。
因此,当 FontSize 更改为 20,然后更改为 30,当 FontSize 设置为 30pt 时,它将调整大小以适应 20pt 字体。
有没有人知道什么可能导致问题以及如何解决?

这是最小示例的代码:

public class TestEditorPanes {
    public static void main(String[] args) {
        JFrame frame = new JFrame();
        frame.add(getPanel());
        frame.setSize(720, 480);
        frame.setVisible(true);
    }

    private static JPanel getPanel() {
        JPanel mainPanel = new JPanel();
        mainPanel.setLayout(new BorderLayout());
        JPanel editorPanel = new ScrollablePanel();
        editorPanel.setLayout(new BoxLayout(editorPanel, BoxLayout.Y_AXIS));

        JTextPane textPane1 = new JTextPane();
        JTextPane textPane2 = new JTextPane();

        editorPanel.add(textPane1);
        editorPanel.add(textPane2);

        mainPanel.add(new JScrollPane(editorPanel), BorderLayout.CENTER);

        // Button sets Font size to 30
        JButton btnFontSize = new JButton("FontSize");
        btnFontSize.addActionListener(e -> {
            MutableAttributeSet attrs = new SimpleAttributeSet();
            StyleConstants.setFontSize(attrs, 30);
            textPane1.setCharacterAttributes(attrs, false);
        });

        mainPanel.add(btnFontSize, BorderLayout.NORTH);

        return mainPanel;
    }

    private static class ScrollablePanel extends JPanel implements Scrollable {

        @Override
        public Dimension getPreferredScrollableViewportSize() {
            return getPreferredSize();
        }

        @Override
        public int getScrollableUnitIncrement(Rectangle visibleRect, int orientation, int direction) {
            return 30;
        }

        @Override
        public int getScrollableBlockIncrement(Rectangle visibleRect, int orientation, int direction) {
            return 30;
        }

        @Override
        public boolean getScrollableTracksViewportWidth() {
            return true;
        }

        @Override
        public boolean getScrollableTracksViewportHeight() {
            return false;
        }

    }
}

【问题讨论】:

    标签: java swing


    【解决方案1】:

    我修改了你的代码来创建这个 GUI。

    我所做的主要更改是在您的ActionListener 中。通过使用StyledDocumentsetCharacterAttributes 方法,我不必先选择文本。似乎设置JTextPane 字体大小导致JTextPane 正确调整大小。

        JButton btnLargeFont = new JButton("Large Font");
        btnLargeFont.addActionListener(e -> {
            StyleConstants.setFontSize(attributes, 30);
            StyledDocument doc = textPane1.getStyledDocument();
            doc.setCharacterAttributes(0, doc.getLength(), attributes, true);
            textPane1.setFont(textPane1.getFont().deriveFont((float) 30));
        });
    

    这是我使用的完整可运行代码。

    import java.awt.BorderLayout;
    import java.awt.Dimension;
    import java.awt.GridLayout;
    
    import javax.swing.BorderFactory;
    import javax.swing.JButton;
    import javax.swing.JFrame;
    import javax.swing.JPanel;
    import javax.swing.JScrollPane;
    import javax.swing.JTextPane;
    import javax.swing.SwingUtilities;
    import javax.swing.text.MutableAttributeSet;
    import javax.swing.text.SimpleAttributeSet;
    import javax.swing.text.StyleConstants;
    import javax.swing.text.StyledDocument;
    
    public class TestEditorPanes implements Runnable {
       
        public static void main(String[] args) {
           SwingUtilities.invokeLater(new TestEditorPanes());
        }
        
        private JFrame frame;
        
        private MutableAttributeSet attributes;
        
        public TestEditorPanes() {
            this.attributes = new SimpleAttributeSet();
        }
        
        @Override
        public void run() {
             frame = new JFrame("Editor Panes");
             frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
             
             frame.add(getPanel(), BorderLayout.CENTER);
             
             frame.pack();
             frame.setLocationByPlatform(true);
             frame.setVisible(true);
        }
    
        private JPanel getPanel() {
            JPanel mainPanel = new JPanel();
            mainPanel.setLayout(new BorderLayout());
            
            JPanel editorPanel = new JPanel();
            editorPanel.setLayout(new BorderLayout());
            editorPanel.setBorder(BorderFactory.createEmptyBorder(10, 10, 10, 10));
            
            JTextPane textPane1 = new JTextPane();
            textPane1.setPreferredSize(new Dimension(400, 150));
            StyleConstants.setFontSize(attributes, 14);
            textPane1.setCharacterAttributes(attributes, true);
            
            JTextPane textPane2 = new JTextPane();
            textPane2.setPreferredSize(new Dimension(400, 150));
    
            editorPanel.add(new JScrollPane(textPane1), BorderLayout.NORTH);
            editorPanel.add(new JScrollPane(textPane2), BorderLayout.SOUTH);
    
            mainPanel.add(new JScrollPane(editorPanel), BorderLayout.CENTER);
    
            JPanel buttonPanel = new JPanel(new GridLayout(0, 1, 10, 10));
            buttonPanel.setBorder(BorderFactory.createEmptyBorder(10, 10, 10, 10));
            
            // Button sets Font size to 14
            JButton btnNormalFont = new JButton("Normal Font");
            btnNormalFont.addActionListener(e -> {
                StyleConstants.setFontSize(attributes, 14);
                StyledDocument doc = textPane1.getStyledDocument();
                doc.setCharacterAttributes(0, doc.getLength(), attributes, true);
                textPane1.setFont(textPane1.getFont().deriveFont((float) 14));
            });
            buttonPanel.add(btnNormalFont, BorderLayout.NORTH);
            
            // Button sets Font size to 30
            JButton btnLargeFont = new JButton("Large Font");
            btnLargeFont.addActionListener(e -> {
                StyleConstants.setFontSize(attributes, 30);
                StyledDocument doc = textPane1.getStyledDocument();
                doc.setCharacterAttributes(0, doc.getLength(), attributes, true);
                textPane1.setFont(textPane1.getFont().deriveFont((float) 30));
            });
            buttonPanel.add(btnLargeFont, BorderLayout.SOUTH);
            
            mainPanel.add(buttonPanel, BorderLayout.NORTH);
    
            return mainPanel;
        }
    
    }
    

    【讨论】:

    • 您的代码工作的原因似乎是您设置了两个 TextPanes 的 preferredSize,因此它们不必调整大小。我试图只使用 StyledDocument 来更改字体大小,但它不起作用。我需要动态调整 textPanes 的大小以仅适合它们包含的文本,因此它们直接位于彼此下方。
    【解决方案2】:

    我找到了一个适合我想要的解决方案。

    当我尝试调试问题时,我注意到呈现组件的 Swing 视图在更改字符属性后由于某种原因没有更新其首选大小。
    在更改 FontSize 后在 JTextPane 上手动调用 updateUI() 会强制 UI 重新加载并计算组件的新大小。
    这是我最小示例的 ActionListener 的工作版本:

            btnFontSize.addActionListener(e -> {
                MutableAttributeSet attrs = new SimpleAttributeSet();
                StyleConstants.setFontSize(attrs, 30);
                textPane1.setCharacterAttributes(attrs, true);
                //Call updateUI to force recalculation of size
                textPane1.updateUI();
            });
    

    Gilbert Le Blancs 的答案也可以,如果我不需要 JTextPanes 直接位于彼此下方并且对我来说似乎是更清洁的解决方案(注意他手动调用 setPreferredSize(),所以 UI 不会必须计算大小)。

    如果有人知道这种行为的原因或更好的解决方案,请告诉我。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2013-08-17
      • 2013-02-08
      • 2017-02-13
      • 1970-01-01
      • 1970-01-01
      • 2014-08-10
      • 1970-01-01
      • 2020-02-21
      相关资源
      最近更新 更多