【问题标题】:JTextArea with line wrap reports incorrect preferred size to JDialog pack?带有换行的 JTextArea 向 JDialog 包报告不正确的首选大小?
【发布时间】:2020-09-19 03:07:08
【问题描述】:

我使用多个 JTextAreas 来显示可变大小的文本,LineWrap 和 WrapStyleWord 都是 true。一个应用程序是在 JScrollPane 内显示具有多个此类文本区域的 JDialog。包含所有这些文本区域的面板实现了 Scrollable(如 here 所解释的),允许 JDialog 具有最大尺寸,但仍会缩小。但是,JDialog 具有设置的水平尺寸。水平滚动条被强制永远不会出现,垂直滚动条不应该出现,除非对话框达到其最大尺寸。但是,在 JTextArea 换行的情况下,当 JDialog 的高度小于其最大值时,会出现垂直滚动条。问题似乎是 JTextArea 在 JDialog 被打包时报告了首选的一行高度,导致对话框的高度小于面板的高度,因为文本区域占用了多行。

这似乎是一个 catch-22,为了获得文本区域的适当高度,需要将其添加到具有所需宽度的面板中以计算换行,这将允许计算实际行数(使用找到的方法here)。但是 JDialog 在调用 pack 之前需要一个首选大小(据我了解),这会导致问题。演示问题:

import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Rectangle;
import javax.swing.BoxLayout;
import javax.swing.JDialog;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTextArea;
import javax.swing.Scrollable;

public class DisplayPopup extends JDialog {

    public DisplayPopup() {

        JPanel actual = new JPanel(); 
        actual.setLayout(new BoxLayout(actual, BoxLayout.Y_AXIS));
        actual.setOpaque(false);

        JTextArea jta = new JTextArea();
        jta.setText("Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed ac dictum magna. Nam sed mattis sem. Donec tortor felis, finibus in lectus fringilla, semper finibus turpis. ");
        jta.setFont(new java.awt.Font("Arial", 0, 18));
        jta.setForeground(new java.awt.Color(204, 204, 204));
        jta.setLineWrap(true);
        jta.setWrapStyleWord(true);
        jta.setEditable(false);
        jta.setBackground(new Color(45,45,45));
        jta.setHighlighter(null);
        actual.add(jta);

        PopupScroll ps = new PopupScroll(actual);

        JScrollPane scroll = new JScrollPane(); 
        scroll.getViewport().add(ps);

        setModalityType(java.awt.Dialog.ModalityType.MODELESS);
        setDefaultCloseOperation(javax.swing.WindowConstants.DISPOSE_ON_CLOSE);
        setResizable(false);
        setLocationRelativeTo(null);
        getContentPane().setLayout(new BorderLayout());
        getContentPane().add(scroll);

        pack();
    }

    public static void launch() {
        DisplayPopup dp = new DisplayPopup();
        dp.setVisible(true);
    }

    public static void main(String[] args) {                                                    
        javax.swing.SwingUtilities.invokeLater(DisplayPopup::launch);                                                                                       
    } 

    private class PopupScroll extends JPanel implements Scrollable {

        public PopupScroll(JPanel jp) {
            setBackground(Color.PINK);
            setLayout(new BorderLayout());
            add(jp);
        }

        @Override
        public Dimension getPreferredScrollableViewportSize() {
            return new Dimension(500,Math.min((int)getPreferredSize().getHeight(),700));
        }

        @Override
        public int getScrollableUnitIncrement(Rectangle arg0, int arg1, int arg2) {
            return 1;
        }

        @Override
        public int getScrollableBlockIncrement(Rectangle arg0, int arg1, int arg2) {
            return 1;
        }

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

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

    }

}

对话框的大小无法包含所有文本行。与this 解决类似问题不同,调用pack 并不能解决问题。我对解决方案有一些猜测,可能会使用 JTextArea 以外的东西(因为我只是想显示文本......?),但我不知道那会是什么。我尝试在垂直滚动条上放置一个层次结构侦听器,这样我就可以检测它何时处于活动状态而不使对话框可见,但这不起作用。这是一个难题还是我遗漏了一些明显的问题?

【问题讨论】:

  • 您应该将 JTextArea 的行和列属性设置为最佳大小,通常在构造函数中完成:new JTextArea(rowCount, columnCount);。通常在将 JScrollPane 放入 GUI 之前,将 JTextAreas 本身放入 JScrollPanes。
  • @DontKnowMuchButGettingBetter 设置行和列确实使其适合特定大小。但是,如果我不知道有多少行,我如何确定正确的行/列值是多少?是的,我知道将每个 JTextArea 放入其自己的 JScrollPane 是很常见的,但我不希望每个单独的文本区域滚动,而是包含所有文本区域的整个面板。

标签: java swing awt jtextarea jdialog


【解决方案1】:

做了一些更改,我看到文本显示在 3 行:

pack();
pack();

是的,这是正确的,我添加了第二个 pack()。第一个包似乎识别宽度,第二个包导致文本被换行。

但是,我也不得不改变:

//actual.setLayout(new BoxLayout(actual, BoxLayout.Y_AXIS));
actual.setLayout(new BorderLayout());

我知道您并不想使用 BorderLayout(因为您希望多个组件垂直对齐),但不知何故,它的首选大小计算似乎有效。

您可以使用GridBagLayout 来代替BoxLayout。它允许您指定“填充”约束。我猜你会尝试只使用水平填充。

或者另一种选择是使用Relative Layout。我会比 GridBagLayout 更容易,因为你不需要担心任何约束。使用它的代码是:

//actual.setLayout(new BoxLayout(actual, BoxLayout.Y_AXIS));
RelativeLayout rl = new RelativeLayout(RelativeLayout.Y_AXIS);
rl.setFill( true );
actual.setLayout( rl );

【讨论】:

  • 边框布局的东西很有趣。很有趣的是,就像其中一个链接的问题一样,解决方案只是简单地添加一个 pack()。我正在使用它的相对布局,它工作得很好。它还解决了我需要在 JTextArea 旁边包含 JLabel 的第二个问题(我也使用 BoxLayout,但现在不再使用了)。我想我也会将您的布局用于其他事情 - 谢谢
  • 我们刚刚遇到了类似的情况,调用 pack() 两次解决了这个问题。在某些 Linux 设备上,same JDialog 类的不同实例,包含一个 JTextArea(未放置在 JScrollBar 中),并将 static final 常量文本设置为内容(阅读:每个实例中完全相同的文本)以完全随机的方式为该 JTextArea 返回至少两个 不同 首选大小。在带有 GridBagLayout 的不可调整大小的 JDialog 中使用它会造成严重破坏,因为在一种情况下它会像 Mikado 棒一样布置组件,但在另一种情况下一切都会好起来的。
猜你喜欢
  • 1970-01-01
  • 2016-11-10
  • 1970-01-01
  • 2014-05-14
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多