【问题标题】:MigLayout resizing with JScrollPaneMigLayout 使用 JScrollPane 调整大小
【发布时间】:2012-06-14 09:25:02
【问题描述】:

我搜索了一下,没有找到一个很好的答案。

我正在开发一个必须可调整大小的 gui。它包含一个状态JTextArea,它位于JScrollPane 中。这是我的问题。只要我不手动调整 JFrame 的大小,就会保留“初始”布局并且一切看起来都很好。一旦我手动调整大小(如果JTextArea 已经处于滚动模式),布局就会混乱。

这是一个 SSCCE(我去掉了大部分部分,同时保留了代码的结构。我希望这样更具可读性):

import java.awt.Color;
import java.awt.Font;

import javax.swing.BorderFactory;
import javax.swing.JComboBox;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JSeparator;
import javax.swing.JSlider;
import javax.swing.JTabbedPane;
import javax.swing.JTextArea;
import javax.swing.JTextField;
import javax.swing.border.TitledBorder;

import net.miginfocom.swing.MigLayout;

public class Tab extends JFrame {
  private static final long serialVersionUID = 1L;
  private JTextArea messageTextArea;
  private JPanel optionPanel, messagePanel;
  private JTabbedPane plotTabPane;

  public static void main(String[] args) {
    final Tab tab = new Tab();
    tab.setSize(1000, 600);
    tab.setVisible(true);
    new Thread(new Runnable() {
      @Override
      public void run() {
        int count = 0;
        tab.printRawMessage("start");
        while (true) {
          try {
            Thread.sleep(200);
          } catch (InterruptedException e) {}
          tab.printRawMessage("\ntestMessage" + count++);
        }
      }
    }).start();
  }

  public Tab() {
    super();
    setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    initComponents();
  }

  private void initComponents() {
    JPanel mainPanel = new JPanel();
    mainPanel.setLayout(new MigLayout("insets 0", "", ""));
    mainPanel.add(getLeftTopPanel(), "shrinky, top, w 450!");
    mainPanel.add(getRightPanel(), "spany 5, wrap, grow, pushx, wmin 400");
    mainPanel.add(getMessagePanel(), "pushy, growy, w 450!");

    JScrollPane contentScrollPane = new JScrollPane(mainPanel);
    contentScrollPane.setBorder(BorderFactory.createEmptyBorder());
    setContentPane(contentScrollPane);
  }

  protected JPanel getLeftTopPanel() {
    if (optionPanel == null) {
      optionPanel = new JPanel();
      optionPanel.setBorder(BorderFactory.createTitledBorder(null, "Configuration", TitledBorder.LEFT, TitledBorder.TOP, new Font("null", Font.BOLD, 12), Color.BLUE));
      optionPanel.setLayout(new MigLayout("insets 0", "", "top, align 50%"));

      JLabel label = new JLabel("Choose");
      label.setHorizontalAlignment(JLabel.RIGHT);
      optionPanel.add(label, "w 65!");
      optionPanel.add(new JSeparator(JSeparator.VERTICAL), "spany 5, growy, w 2!");
      optionPanel.add(new JComboBox(new String[] {"option1", "option2", "option3"}), "span, growx, wrap");

      optionPanel.add(new JLabel("Type"), "right");
      optionPanel.add(new JTextField("3"), "w 65!, split 2");
      optionPanel.add(new JLabel("Unit"), "wrap");

      optionPanel.add(new JLabel("Slide"), "right");
      optionPanel.add(new JSlider(0, 100), "span, growx, wrap");
    }
    return optionPanel;
  }

  protected JTabbedPane getRightPanel() {
    if (plotTabPane == null) {
      plotTabPane = new JTabbedPane();
      plotTabPane.add("Tab1", new JPanel());
      plotTabPane.add("Tab2", new JPanel());
    }
    return plotTabPane;
  }

  protected JPanel getMessagePanel() {
    if (messagePanel == null) {
      messagePanel = new JPanel();
      messagePanel.setBorder(BorderFactory.createTitledBorder(null, "Status Console", TitledBorder.LEFT, TitledBorder.TOP, new Font("null", Font.BOLD, 12), Color.BLUE));
      messagePanel.setLayout(new MigLayout("insets 0", "", "top, align 50%"));
      messagePanel.add(new JScrollPane(getMessageTextArea()), "push, grow");
    }
    return messagePanel;
  }

  protected JTextArea getMessageTextArea() {
    if (messageTextArea == null) {
      messageTextArea = new JTextArea();
      messageTextArea.setEditable(false);
      messageTextArea.setFont(new Font(null, Font.PLAIN, 20));
      messageTextArea.setBorder(BorderFactory.createEmptyBorder(4, 4, 4, 4));
    }
    return messageTextArea;
  }

  public void printRawMessage(String rawMessage) {
    getMessageTextArea().append(rawMessage);
    getMessageTextArea().setCaretPosition(getMessageTextArea().getText().length());
  }
}

布局的东西基本上发生在initComponents()

查看问题:

  1. 启动应用程序(我使用 miglayout-4.0-swing.jar)。
  2. 稍等片刻(不要调整窗口大小),直到有足够的消息在状态文本区域中创建滚动条。
  3. 现在这就是我想要的。 JTextArea 一直到 JFrame 的底部,并在必要时滚动。
  4. 现在调整窗口大小。如您所见,一切都变得一团糟。只有最大化窗口才可以。

这里有两张截图。第一个是我想要的样子: 第二个是调整大小后:

我的问题:有人可以告诉我,我如何在调整大小之前保持布局不变?我想让JTextArea 一直到窗口底部。如果有必要,滚动条应该会出现。如果窗口太小(因为配置面板的高度是固定的),状态面板可以低于窗口底部的唯一方法。

我希望我说清楚了。如果没有,请询​​问。 ;)

编辑:如果您删除顶部 JScrollPanel(包含所有组件的那个),您可以看到我想要的行为。换个方式

JScrollPane contentScrollPane = new JScrollPane(mainPanel);
contentScrollPane.setBorder(BorderFactory.createEmptyBorder());
setContentPane(contentScrollPane);

setContentPane(mainPanel);

看看我的意思。不幸的是,如果窗口非常小,这样我会松开滚动条。

【问题讨论】:

    标签: java swing miglayout


    【解决方案1】:

    专注于您的状态区域并使用嵌套布局会产生如下所示的结果。特别注意,

    • 使用invokeLater() 在 EDT 上构建 GUI。
    • 使用 javax.swing.Timer 更新 EDT 上的 GUI。
    • 使用pack() 使窗口适合其子组件的首选大小和布局。
    • 使用DefaultCaret的更新策略来控制滚动。
    • 避免在公共访问器中进行不必要的惰性实例化。
    • 避免setXxxSize();明智地覆盖getXxxSize()
    • 严格审查延长 JFrame 的决定。

    import java.awt.Color;
    import java.awt.EventQueue;
    import java.awt.Font;
    import java.awt.GridLayout;
    import java.awt.event.ActionEvent;
    import java.awt.event.ActionListener;
    import javax.swing.BorderFactory;
    import javax.swing.Box;
    import javax.swing.BoxLayout;
    import javax.swing.JComboBox;
    import javax.swing.JFrame;
    import javax.swing.JLabel;
    import javax.swing.JPanel;
    import javax.swing.JScrollPane;
    import javax.swing.JSeparator;
    import javax.swing.JTabbedPane;
    import javax.swing.JTextArea;
    import javax.swing.JTextField;
    import javax.swing.Timer;
    import javax.swing.border.TitledBorder;
    import javax.swing.text.DefaultCaret;
    
    public class Tab extends JFrame {
    
        private JTextArea messageTextArea;
        private JPanel optionPanel, messagePanel;
        private JTabbedPane plotTabPane;
    
        public static void main(String[] args) {
            EventQueue.invokeLater(new Runnable() {
    
                @Override
                public void run() {
    
                    final Tab tab = new Tab();
                    tab.setVisible(true);
                    Timer t = new Timer(200, new ActionListener() {
    
                        int count = 0;
    
                        @Override
                        public void actionPerformed(ActionEvent e) {
                            tab.printRawMessage("testMessage" + count++);
                        }
                    });
                    t.start();
                }
            });
        }
    
        public Tab() {
            initComponents();
        }
    
        private void initComponents() {
            this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
            JPanel mainPanel = new JPanel(new GridLayout(1, 0));
            Box leftPanel = new Box(BoxLayout.Y_AXIS);
            leftPanel.add(getLeftTopPanel());
            leftPanel.add(getMessagePanel());
            mainPanel.add(leftPanel);
            mainPanel.add(getRightPanel());
            this.add(mainPanel);
            this.pack();
            this.setLocationRelativeTo(null);
        }
    
        protected JPanel getLeftTopPanel() {
            optionPanel = new JPanel();
            optionPanel.setBorder(BorderFactory.createTitledBorder(null,
                "Configuration", TitledBorder.LEFT, TitledBorder.TOP,
                new Font("null", Font.BOLD, 12), Color.BLUE));
            JLabel label = new JLabel("Choose");
            label.setHorizontalAlignment(JLabel.RIGHT);
            optionPanel.add(label);
            optionPanel.add(new JSeparator(JSeparator.VERTICAL));
            optionPanel.add(new JComboBox(
                new String[]{"option1", "option2", "option3"}));
            optionPanel.add(new JLabel("Type"));
            optionPanel.add(new JTextField("3"));
            return optionPanel;
        }
    
        protected JTabbedPane getRightPanel() {
            plotTabPane = new JTabbedPane();
            plotTabPane.add("Tab1", new JPanel());
            plotTabPane.add("Tab2", new JPanel());
            return plotTabPane;
        }
    
        protected JPanel getMessagePanel() {
            messagePanel = new JPanel(new GridLayout());
            messagePanel.setBorder(BorderFactory.createTitledBorder(null,
                "Status Console", TitledBorder.LEFT, TitledBorder.TOP,
                new Font("null", Font.BOLD, 12), Color.BLUE));
            final JScrollPane sp = new JScrollPane(getMessageTextArea());
            sp.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_ALWAYS);
            messagePanel.add(sp);
            return messagePanel;
        }
    
        protected JTextArea getMessageTextArea() {
            messageTextArea = new JTextArea("", 10, 19);
            messageTextArea.setEditable(false);
            messageTextArea.setFont(new Font(null, Font.PLAIN, 20));
            messageTextArea.setBorder(BorderFactory.createEmptyBorder(4, 4, 4, 4));
            DefaultCaret caret = (DefaultCaret) messageTextArea.getCaret();
            caret.setUpdatePolicy(DefaultCaret.ALWAYS_UPDATE);
            return messageTextArea;
        }
    
        public void printRawMessage(String rawMessage) {
            messageTextArea.append(rawMessage + "\n");
        }
    }
    

    【讨论】:

    • 哇,非常感谢大家的意见。我从来没有真正“学会”如何正确编码大型 gui,所以这对我有很大帮助。 :)
    • 关于“严格检查扩展 JFrame 的决定”:在我的应用程序中,我有几个位于单个 JFrame 中的选项卡(扩展 JPanel,我只是为这个 SSCCE 更改了它)(嗯,扩展名)。我在这个扩展的 JFrame 中有很多方法,例如 addPlotData()setSelectionList() 和类似的东西,它们控制 gui 显示的内容。拥有某种 GuiManager 类来保存这个 JFrame 并执行所有操作会更好吗?或者首选的方法是什么?
    • 哦,“避免公共访问器中不必要的惰性实例化”是什么意思?
    • 你的主类has-a JFrame;除非它覆盖了 JFrame 方法,否则它可能不是 is-a JFrame。你有几个方法从if (xPanel == null)开始;我不明白他们为什么是protected
    • 我对它们进行了保护,因为我在本课程之外不需要它们。但我在子类中需要它们(例如更改边框)。
    【解决方案2】:

    initComponents 方法中为您的mainPanel 添加大小限制。例如:

        mainPanel.setMinimumSize(new Dimension(400, 400));
        mainPanel.setPreferredSize(new Dimension(400, 400));
        mainPanel.setMaximumSize(new Dimension(400, 400));
    

    【讨论】:

    • 嗯,我可以这样做,但是我必须为调整窗口大小创建一个侦听器,并将约束设置为可用的窗口大小。此外,这会导致小窗口一团糟(所以我的固定大小的配置面板不会完全适合JFrame...我认为必须有一种方法可以仅通过布局实现这一点。但感谢您的贡献,我也想过,只是觉得很乱。
    • 我可能没理解对,但我认为你不需要倾听者。这个想法只是指定您希望滚动窗格出现的大小。这里我取了 400*400 但如果你认为它太小/太大,你可以改变它。您也可以使用子组件大小的总和来设置它。
    • 是的,这适用于最小尺寸,但我的问题(在第二个屏幕截图中可以看到,状态面板在调整大小时扩展得很远)仍然存在。
    猜你喜欢
    • 2019-04-09
    • 2015-02-07
    • 2011-06-24
    • 1970-01-01
    • 1970-01-01
    • 2011-06-17
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多