java - MigLayout 使用 JScrollPane 调整大小

标签 java swing miglayout

我搜索了一下,没有找到适合我的问题的答案。

我正在开发一个必须可调整大小的图形用户界面。它包含一个位于 JScrollPane 内的状态 JTextArea。这是我的问题。只要我不手动调整 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. 现在调整窗口大小。如您所见,一切都变得一团糟。只有当窗口最大化时才好。

这是两个屏幕截图。第一个是我想要的样子: Before resizing: good! 第二个是调整大小后: After resizing: bad!

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

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

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

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

setContentPane(mainPanel);

明白我的意思。不幸的是,如果窗口非常小,我会通过这种方式松开滚动条。

最佳答案

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

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

image

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");
    }
}

关于java - MigLayout 使用 JScrollPane 调整大小,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/11030191/

相关文章:

c# - 漂亮的、对设计师友好的 GUI 库

java - GWT MenuItem-列出选定的值

java - 如何在jDisktopPane中显示JinternalFrame,但该DisktopPane包含GridLayout?

java图像透明度(图像丢失)

java - 当 MigLayout 的 JLabel 文本太长时,如何自动中断 JLabel 的文本?

java - 使用 miglayout 的垂直按钮栏布局

java - 防止 MigLayout 组件间隔开

java - 在 ANDROID 中后台运行时出现 java.lang.RunTimeException

java - 如何将一个 Pane 连接到另一个 Pane

java - Volley 在模拟器上运行但在设备上失败