【问题标题】:JScrollPane shrink/collapse on window minimizeJScrollPane 在窗口最小化时收缩/折叠
【发布时间】:2015-07-07 14:44:26
【问题描述】:

我有一个包含 JTextArea 的 JScrollPane。当窗口最小化然后恢复时,JScrollPane 将自行折叠。请注意,仅当 JTextArea 中的文本超过 JTextArea 的给定宽度和/或高度(即出现水平或垂直滚动​​条)时,才会发生这种挤压。

这里的这个问题:JScrollpane loses size after minimize 提出了同样的问题,但该问题从未得到解决,除了向 JScrollPane 添加 weightx、weighty 和 fill 约束,我已经开始这样做了。

以下是演示问题的简化示例。窗口最小化和恢复后,如何让 JScrollPane 保持其大小?

import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.IOException;
import java.util.List;
import java.util.concurrent.ExecutionException;
import javax.swing.BorderFactory;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTextArea;
import javax.swing.SwingUtilities;
import javax.swing.SwingWorker;
import javax.swing.border.EtchedBorder;

public class GUITest implements ActionListener {

    JButton button = new JButton("Button");
    JTextArea textArea = new JTextArea();
    SwingWorker<String, String> mySwingWorker = null;

    public static void main(String[] args) throws IOException {
        GUITest tracer = new GUITest();
    }

    public GUITest() throws IOException {
        SwingUtilities.invokeLater(new Runnable() {
            public void run() {
                try {
                    createAndShowGUI();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        });
    }

    public void createAndShowGUI() throws IOException {
        JFrame frame = new JFrame("GUI Test");
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.setResizable(false);
        JPanel mainPanel = new JPanel();
        mainPanel.setLayout(new GridBagLayout());
        GridBagConstraints mainPanelConstraints = new GridBagConstraints();
        mainPanelConstraints.gridx = 0;
        mainPanelConstraints.gridy = 0;
        mainPanelConstraints.fill = GridBagConstraints.BOTH;
        button.addActionListener(this);
        mainPanel.add(button, mainPanelConstraints);
        mainPanelConstraints.gridx = 0;
        mainPanelConstraints.gridy = 1;
        mainPanelConstraints.fill = GridBagConstraints.BOTH;
        mainPanel.add(buildTextAreaPanel(), mainPanelConstraints);
        frame.getContentPane().add(mainPanel);
        frame.pack();
        frame.setVisible(true);
    }

    private JPanel buildTextAreaPanel() {
        JPanel textAreaPanel = new JPanel();
        textAreaPanel.setLayout(new GridBagLayout());
        GridBagConstraints textAreaPanelConstraints = new GridBagConstraints();
        textAreaPanel.setBorder(BorderFactory.createTitledBorder(new EtchedBorder(EtchedBorder.RAISED), "TextArea"));
        textArea.setColumns(30);
        textArea.setRows(15);
        textArea.setEditable(false);
        JScrollPane textAreaScrollPane = new JScrollPane(textArea);
        textAreaScrollPane.setHorizontalScrollBarPolicy(JScrollPane.HORIZONTAL_SCROLLBAR_AS_NEEDED);
        textAreaScrollPane.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED);
        textAreaPanelConstraints.gridx = 0;
        textAreaPanelConstraints.gridy = 0;
        textAreaPanelConstraints.weightx = 1.0;
        textAreaPanelConstraints.weighty = 1.0;
        textAreaPanelConstraints.fill = GridBagConstraints.BOTH;
        textAreaPanel.add(textAreaScrollPane, textAreaPanelConstraints);
        return textAreaPanel;
    }

    public void actionPerformed(ActionEvent e) {
        if (e.getSource() == button) {
            mySwingWorker = new MySwingWorker();
            mySwingWorker.execute();
        }
    }

    private class MySwingWorker extends SwingWorker<String, String> {
        public String doInBackground() throws Exception {
            for (int i = 0; i < textArea.getRows(); i++) {
                publish("text\n");
            }
            publish("more text\n");

            return "Done.";
        }
        public void process(List<String> chunks) {
            for (String msg : chunks) {
                textArea.append(msg);
            }
        }   
        public void done() {
            try {
                String msg = get();
                textArea.append("\n" + msg);
            } catch (InterruptedException e) {
                e.printStackTrace();
            } catch (ExecutionException e) {
                e.printStackTrace();
            }
        }

    }

}

【问题讨论】:

    标签: java swing jscrollpane jtextarea


    【解决方案1】:

    您需要在两种布局中设置 weightx 和 weighty。在 createAndShowGui 中,将 mainPanelConstraints.weightx 和 mainPanelConstraints.weighty 设置为 1,然后再添加文本区域面板。

    【讨论】:

    • 我可以看到这会产生所需的行为,但我不明白为什么修改容器的权重值会影响其子组件的大小行为。你能解释一下吗?
    • 编辑上述评论:“子组件”实际上是指“孙子组件”。给出错误行为的 textAreaScrollPane 位于 textAreaPanel 中,然后 textAreaPanel 位于 mainPanel 中。那么,更改 mainPanel 的布局属性对 textAreaScrollPane 有何直接影响?
    • weightx 和 weighty 约束确定组件是否扩展以填充其父级。文本区域面板是否拉伸以填充 mainPanel 无关紧要,除非 mainPanel 本身拉伸以填充窗口。
    【解决方案2】:

    我通过删除所有 GridBagLayouts 并用 BorderLayouts 替换它们解决了这个特定代码示例中的收缩问题。我不知道为什么 JScrollPane 会像您在使用 GridBagLayout 时发现的那样做出反应。

    我觉得从类构造函数启动事件调度线程真的很不舒服。我将 SwingUtilities invokeLater 移到 main 方法中。

    无论如何,这是代码。

    package com.ggl.testing;
    
    import java.awt.BorderLayout;
    import java.awt.event.ActionEvent;
    import java.awt.event.ActionListener;
    import java.io.IOException;
    import java.util.List;
    import java.util.concurrent.ExecutionException;
    
    import javax.swing.BorderFactory;
    import javax.swing.JButton;
    import javax.swing.JFrame;
    import javax.swing.JPanel;
    import javax.swing.JScrollPane;
    import javax.swing.JTextArea;
    import javax.swing.SwingUtilities;
    import javax.swing.SwingWorker;
    import javax.swing.border.EtchedBorder;
    
    public class JScrollPaneTest implements ActionListener {
    
        JButton button = new JButton("Button");
        JTextArea textArea = new JTextArea();
        SwingWorker<String, String> mySwingWorker = null;
    
        public static void main(String[] args) throws IOException {
            SwingUtilities.invokeLater(new Runnable() {
                public void run() {
                    try {
                        new JScrollPaneTest().createAndShowGUI();
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }
            });
    
        }
    
        public JScrollPaneTest() throws IOException {
    
        }
    
        public void createAndShowGUI() throws IOException {
            JFrame frame = new JFrame("JScrollPane Test");
            frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
            frame.setResizable(true);
    
            JPanel mainPanel = new JPanel();
            mainPanel.setLayout(new BorderLayout());
            mainPanel.add(button, BorderLayout.NORTH);
            mainPanel.add(buildTextAreaPanel(), BorderLayout.CENTER);
    
            button.addActionListener(this);
    
            frame.add(mainPanel, BorderLayout.CENTER);
            frame.pack();
            frame.setVisible(true);
        }
    
        private JPanel buildTextAreaPanel() {
            JPanel textAreaPanel = new JPanel();
            textAreaPanel.setLayout(new BorderLayout());
            textAreaPanel.setBorder(BorderFactory.createTitledBorder(
                    new EtchedBorder(EtchedBorder.RAISED), "TextArea"));
    
            textArea.setColumns(30);
            textArea.setRows(15);
            textArea.setEditable(false);
    
            JScrollPane textAreaScrollPane = new JScrollPane(textArea);
    
            textAreaPanel.add(textAreaScrollPane, BorderLayout.CENTER);
            return textAreaPanel;
        }
    
        public void actionPerformed(ActionEvent e) {
            if (e.getSource() == button) {
                mySwingWorker = new MySwingWorker();
                mySwingWorker.execute();
            }
        }
    
        private class MySwingWorker extends SwingWorker<String, String> {
            public String doInBackground() throws Exception {
                for (int i = 0; i < textArea.getRows(); i++) {
                    publish("text\n");
                }
                publish("more text\n");
    
                return "Done.";
            }
    
            public void process(List<String> chunks) {
                for (String msg : chunks) {
                    textArea.append(msg);
                }
            }
    
            public void done() {
                try {
                    String msg = get();
                    textArea.append("\n" + msg);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                } catch (ExecutionException e) {
                    e.printStackTrace();
                }
            }
    
        }
    
    }
    

    【讨论】:

    • 感谢有关将 EDT 开始移动到主要方法的提示。在弄乱了您的示例版本之后,我发现只需将 mainPanel 的布局从 GridBagLayout 更改为 BorderLayout。 textAreaPanel(其中存在侵权的 JScrollPane)可以保持设置为 GridBagLayout 并实现所需的行为。这引发了我对这个问题的其他两个工作答案的相同问题:为什么更改组件的布局配置不会影响其子组件的行为,而是影响下面多层的组件?
    • @FalconRunner11:除了维护 Swing 的 Oracle 人员之外,任何人都很难回答你的问题。我发现通过将 Swing 组件放在主 JPanel 中并将该 JPanel 放在 JFrame 中可以防止很多问题。通常,为了防止出现不同类型的问题,我会为 GridBagLayout 中的每个 Swing 组件创建一个新的 GridBagConstraints。
    • 我认为 VGR 的上述回应提供了足够好的解释。 textAreaPanel 在添加到 mainPanel 时应该具有附加的 weightx 和 weighty 约束,以便处理其自身可能发生的任何调整大小。
    【解决方案3】:

    将父Container(内容窗格)的布局设置为FlowLayout

    ...
    frame.getContentPane().setLayout(new FlowLayout());
    frame.getContentPane().add(mainPanel);
    ...
    

    【讨论】:

    • 此解决方案似乎有效,但我不明白为什么在这种情况下更改 JFrame 内容窗格的布局会影响其下方几层的组件的行为。你能解释一下这里发生了什么吗?
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-05-29
    • 2016-01-18
    • 1970-01-01
    • 2018-08-24
    • 2012-03-21
    相关资源
    最近更新 更多