简体   繁体   English

使用JTextArea时,MigLayout在JOption窗格中导致异常。 FlowLayout很好

[英]MigLayout causes exception in JOption pane when JTextArea is used. FlowLayout is just fine

The sample program below displays a frame with two buttons. 下面的示例程序显示带有两个按钮的框架。 Pressing the second button which uses MigLayout causes an exception. 按下使用MigLayout的第二个按钮将导致异常。 The first button which uses FlowLayout works just fine. 使用FlowLayout的第一个按钮可以正常工作。 Appears to be a bug in MigLayout ? 似乎是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);
}
}

The reason for the NPE is some mig-internal magic related to JTextArea/JEditorPane (details below). NPE的原因是与JTextArea / JEditorPane有关的一些内部迁移魔术(详细信息如下)。 This may cause problems in nested containers some parent has a manager that keeps internal state, fi in BoxLayout. 这可能会在嵌套容器中引起问题,某些父级具有在BoxLayout中保留内部状态fi的管理器。

Simplest way out: don't nest - MigLayout is designed to control one-big-panel. 最简单的出路:不要嵌套-MigLayout旨在控制一个大面板。 In the case of adding to pre-fabricated special-case containers like optionPane, that's not an option: here it helps to wrap the textArea/editorPane in a JScrollPane. 在添加到诸如optionPane的预制特殊情况容器的情况下,这不是一个选择:这里有助于将textArea / editorPane包装在JScrollPane中。

Not entirely sure if that's a bug, though manipulating the container hierarchy upwards in mere getter (preferredLayouutSize) looks suspicious. 尽管仅使用getter(preferredLayouutSize)来向上操纵容器层次结构似乎很可疑,但不能完全确定这是否是一个错误。

Details of why the NPE happens: magic's effect is to force the parent container layout on querying the prefSize: NPE发生原因的详细信息:魔术的作用是在查询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);
    }
}

Which in turn re-queries the top-level container's prefSize (down in adjustWindowSize): 依次重新查询顶级容器的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();
    ....
}

When nested inside a BoxLayout, this leads to re-entering the checkTarget: 当嵌套在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);
        }

     }
 }   

MigLayout 5.0 has compensation for this. MigLayout 5.0为此进行了补偿。 It's in the trunk at Google Code if you want to test it. 如果要测试,它位于Google Code的主干中。

Cheers, Mikael 干杯,米凯尔

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM