2

以下のサンプルプログラムは、2つのボタンを持つフレームを表示します。を使用する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に関連する移行内部の魔法です(詳細は以下を参照)。これにより、ネストされたコンテナーで問題が発生する可能性があります。一部の親には、BoxLayoutの内部状態fiを保持するマネージャーがあります。

最も簡単な方法:ネストしないでください-MigLayoutは、1つの大きなパネルを制御するように設計されています。optionPaneのような事前に作成された特殊なケースのコンテナーに追加する場合、それはオプションではありません。ここでは、textArea/editorPaneをJScrollPaneでラップするのに役立ちます。

それがバグであるかどうかは完全にはわかりませんが、単なるゲッター(preferredLayouutSize)でコンテナー階層を上向きに操作することは疑わしいように見えます。

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

次に、最上位のコンテナの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

MigLayout5.0にはこれに対する補償があります。テストしたい場合は、GoogleCodeのトランクにあります。

乾杯、ミカエル

于 2013-03-19T13:37:36.707 に答える