[英]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.