2

下面的示例程序显示了一个带有两个按钮的框架。按下使用的第二个按钮MigLayout会导致异常。使用的第一个按钮FlowLayout工作得很好。似乎是一个错误MigLayout

import java.awt.FlowLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;

import javax.swing.JButton;
import javax.swing.JDialog;
import javax.swing.JFrame;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JTextArea;
import javax.swing.SwingUtilities;

import net.miginfocom.swing.MigLayout;

public class Main extends JPanel {

static private JFrame frame = new JFrame("Test MigLayout");

public Main() {
    JButton flowLayoutButton = new JButton("FlowLayout");
    JButton miglayoutButton = new JButton("MigLayout");
    add(flowLayoutButton);
    add(miglayoutButton);
    flowLayoutButton.addActionListener(new ActionListener() {
        public void actionPerformed(ActionEvent actionEventIn) {
            showDialog(false);
        }
    });
    miglayoutButton.addActionListener(new ActionListener() {
        public void actionPerformed(ActionEvent actionEventIn) {
            showDialog(true);
        }
    });
}

public static void main(String[] args) {
    Main main = new Main();
    frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    frame.setContentPane(main);
    SwingUtilities.invokeLater(new Runnable() {
        @Override
        public void run() {
            frame.pack();
            frame.setVisible(true);
            frame.setBounds(200, 200, 200, 200);
        }
    });
}
private void showDialog(boolean useMidgLayoutIn) {
    JPanel panel = new JPanel(useMidgLayoutIn ? new MigLayout() : new FlowLayout());
    JTextArea topTextArea = new JTextArea("Here is some junk text to fill up the Text Area.");
    panel.add(topTextArea);
    panel.add(new JButton("Button"));

    JOptionPane optionPane = new JOptionPane(panel, JOptionPane.ERROR_MESSAGE);
    JDialog dialog = optionPane.createDialog(frame, "Application  Error");
    dialog.setResizable(true);
    dialog.pack();
    dialog.setVisible(true);
}
}
4

2 回答 2

3

NPE 的原因是一些与 JTextArea/JEditorPane 相关的 mig 内部魔法(详情如下)。这可能会导致嵌套容器出现问题,某些父级具有保持内部状态的管理器,即 BoxLayout 中的 fi。

最简单的出路:不要嵌套 - MigLayout 旨在控制一个大面板。在添加到像 optionPane 这样的预制特殊情况容器的情况下,这不是一个选项:这里它有助于将 textArea/editorPane 包装在 JScrollPane 中。

不完全确定这是否是一个错误,尽管仅在 getter (preferredLayouutSize) 中向上操作容器层次结构看起来很可疑。

NPE 发生原因的详细信息:magic 的效果是强制父容器布局查询 prefSize:

public Dimension preferredLayoutSize(Container parent) {
    synchronized (parent.getTreeLock()) {
        if (lastParentSize == null
                || !parent.getSize().equals(lastParentSize)) {
            for (ComponentWrapper wrapper : ccMap.keySet()) {
                Component c = (Component) wrapper.getComponent();
                if (c instanceof JTextArea
                        || c instanceof JEditorPane
                        || (c instanceof JComponent && Boolean.TRUE
                                .equals(((JComponent) c)
                                        .getClientProperty("migLayout.dynamicAspectRatio")))) {
                    layoutContainer(parent);
                    break;
                }
            }
        }

        lastParentSize = parent.getSize();
        return getSizeImpl(parent, LayoutUtil.PREF);
    }
}

进而重新查询顶级容器的 prefSize(在 adjustWindowSize 中向下):

private void adjustWindowSize(ContainerWrapper parent) {
    BoundSize wBounds = lc.getPackWidth();
    BoundSize hBounds = lc.getPackHeight();

    if (wBounds == null && hBounds == null)
        return;

    Window win = ((Window) SwingUtilities.getAncestorOfClass(Window.class,
            (Component) parent.getComponent()));
    if (win == null)
        return;

    Dimension prefSize = win.getPreferredSize();
    ....
}

当嵌套在 BoxLayout 中时,这会导致重新输入 checkTarget:

public Dimension preferredLayoutSize(Container target) {
    Dimension size;
    synchronized(this) {
        checkContainer(target);
        // checkRequests initializes all internal book-keeping
        checkRequests();
        // here's the NPE in the second round
        size = new Dimension(xTotal.preferred, yTotal.preferred);
    }
    ...
 }

void checkRequests() {
    // config only if not yet done earlier
    if (xChildren == null || yChildren == null) {
        // The requests have been invalidated... recalculate
        // the request information.
        int n = target.getComponentCount();
        //JW: first time around the arrays of sz are initialized
        xChildren = new SizeRequirements[n];
        yChildren = new SizeRequirements[n];
        for (int i = 0; i < n; i++) {
            Component c = target.getComponent(i);
            ....
            Dimension min = c.getMinimumSize();
            Dimension typ = c.getPreferredSize();
            ....
        }
        .... 
        // JW: never reached if c.getPref re-enters, that is xTotal remains null
        if (absoluteAxis == X_AXIS) {
            xTotal = SizeRequirements.getTiledSizeRequirements(xChildren);
            yTotal = SizeRequirements.getAlignedSizeRequirements(yChildren);
        } else {
            xTotal = SizeRequirements.getAlignedSizeRequirements(xChildren);
            yTotal = SizeRequirements.getTiledSizeRequirements(yChildren);
        }

     }
 }   
于 2013-03-18T16:44:06.560 回答
1

MigLayout 5.0 对此进行了补偿。如果您想对其进行测试,它位于 Google Code 的后备箱中。

干杯,米凯尔

于 2013-03-19T13:37:36.707 回答