In out software we have a very complex window where the is a set of JPanels kept like and accordion. When user clicks on one of the accordion title, the relative JPanel opens and it is displayed. Unfortunately, these JPanels contain a sub panel with some images previews whose dimensions and arrangements are calculated when the parent is initialized. Anyway, as the calculus is done in constructor, the parent contaier will have a width of 0 as it has not still been displayed so those images will have a negative width (according the used formula) . So, when the JPanel is finally open no image is actually displayed. Here's a SSCC example:
public class Main {
public static void main(String[] args) {
try {
SwingUtilities.invokeAndWait(new Runnable() {
@Override
public void run() {
createGUI();
}
});
} catch (InvocationTargetException e| InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
private static void createGUI() {
final JFrame mainFrame = new JFrame("Main");
mainFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
mainFrame.setResizable(true);
JPanel mainContainer = new JPanel(new BorderLayout());
JPanel north = new JPanel(new GridBagLayout());
north.setBorder(BorderFactory.createTitledBorder("Title"));
for(int i=0; i<10; i++) {
JLabel label = new JLabel("Info "+i);
if (i%2 == 0) {
Font f = label.getFont();
label.setFont(f.deriveFont(f.getStyle() ^ Font.BOLD));
}
else
label.setBorder(BorderFactory.createLineBorder(Color.BLACK));
switch(i) {
case 0:
north.add(label, new GridBagConstraints(0, 0, 2, 1, 0, 0, GridBagConstraints.FIRST_LINE_START, 0, new Insets(0, 0, 0, 10), 0, 0));
break;
case 1:
label.setText(label.getText()+" aggiunta");
north.add(label, new GridBagConstraints(0, 1, 2, 1, 1.0, 0, GridBagConstraints.CENTER, GridBagConstraints.HORIZONTAL, new Insets(0, 0, 0, 0), 0, 0));
break;
case 2:
north.add(label, new GridBagConstraints(0, 2, 1, 1, 1.0, 0, GridBagConstraints.LINE_START, GridBagConstraints.HORIZONTAL, new Insets(10, 0, 0, 10), 0, 0));
break;
case 3:
north.add(label, new GridBagConstraints(0, 3, 1, 1, 1.0, 0, GridBagConstraints.LINE_START, GridBagConstraints.HORIZONTAL, new Insets(0, 0, 0, 10), 0, 0));
break;
case 4:
north.add(label, new GridBagConstraints(1, 2, 1, 1, 1.0, 0, GridBagConstraints.LINE_END, GridBagConstraints.HORIZONTAL, new Insets(10, 0, 0, 0), 0, 0));
break;
case 5:
north.add(label, new GridBagConstraints(1, 3, 1, 1, 1.0, 0, GridBagConstraints.LINE_END, GridBagConstraints.HORIZONTAL, new Insets(0, 0, 0, 0), 0, 0));
break;
case 6:
north.add(label, new GridBagConstraints(0, 4, 1, 1, 1.0, 0, GridBagConstraints.LAST_LINE_START, GridBagConstraints.HORIZONTAL, new Insets(10, 0, 0, 10), 0, 0));
break;
case 7:
north.add(label, new GridBagConstraints(0, 5, 1, 1, 1.0, 0, GridBagConstraints.LAST_LINE_START, GridBagConstraints.HORIZONTAL, new Insets(0, 0, 5, 10), 0, 0));
break;
case 8:
north.add(label, new GridBagConstraints(1, 4, 1, 1, 1.0, 0, GridBagConstraints.LAST_LINE_END, GridBagConstraints.HORIZONTAL, new Insets(10, 0, 0, 0), 0, 0));
break;
case 9:
north.add(label, new GridBagConstraints(1, 5, 1, 1, 1.0, 0, GridBagConstraints.LAST_LINE_END, GridBagConstraints.HORIZONTAL, new Insets(0, 0, 5, 0), 0, 0));
break;
}
}
final JPanel sud = new JPanel(new GridBagLayout());
loadImages(sud, -1);
JSplitPane split = new JSplitPane(JSplitPane.VERTICAL_SPLIT, north, new JScrollPane(sud));
split.setOneTouchExpandable(true);
mainContainer.add(split, BorderLayout.CENTER);
mainFrame.getContentPane().add(mainContainer);
mainFrame.pack();
mainFrame.setVisible(true);
}
private static void loadImages(JPanel sud, int width) {
String[] imagesPath = new String[]{"https://i.stack.imgur.com/ynit7.png",
"https://i.stack.imgur.com/ETMo5.png",
"https://i.stack.imgur.com/ABdpC.png"};
for (int j = 0; j<imagesPath.length; j++) {
try {
JPanel panelImage = new JPanel(new BorderLayout());
panelImage.add(new JButton("X"), BorderLayout.EAST);
BufferedImage original = ImageIO.read(new URL(imagesPath[j]));
BufferedImage resized = new BufferedImage(200, 100, original.getType());
Graphics2D g2d = resized.createGraphics();
g2d.drawImage(original, 0, 0, 200, 100, null);
g2d.dispose();
JLabel labelImg = new JLabel(new ImageIcon(resized));
labelImg.setPreferredSize(new Dimension(width, 100));
labelImg.setSize(new Dimension(width, 100));
panelImage.add(labelImg, BorderLayout.CENTER);
sud.add(panelImage,
new GridBagConstraints(0, j, 2, 1, 0, 0, GridBagConstraints.CENTER, GridBagConstraints.BOTH, new Insets(0, 0, 0, 0), 0, 0));
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
I know this example is stupid but it reflects the basic operation. Consider I have many sud-like JPanels
initialized like the following and when they are expanded from the accordion, no image is showed because given width to loadImages()
was negative.
So, I need a way to recalculate images as soon as JPanel has finished expading and is visible so that that method can get its actual real width and rearrange images. This is a very annoying bug we have in our product caused by previous members of my team and I was asked to solve it. Actually, when after accordion has opened, our clients click on a radio button and images are reloaded but they want us to do it automatically. I would like to add an AncestorListener
and override ancestorAdded()
method into which I would reload images but they say we already have too many listeners in this window.
Is there a better solution?
For "accordion" I mean something similar:
and when user clicks on it, another very complex JPanel is expanded and showed, with a sub-panel with many images previews. Some images examples are: