0

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: enter image description here

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:

1.png 2.png 3.png

SagittariusA
  • 5,289
  • 15
  • 73
  • 127
  • 1
    `String[] imagesPath = new String[]{"C:/Users/.../1.png", ..` One way to get image(s) for a (self contained) example is to hot link to images seen in [this Q&A](http://stackoverflow.com/q/19209650/418556). – Andrew Thompson Feb 09 '18 at 15:44
  • I've just added some images like the ones we get in out software – SagittariusA Feb 09 '18 at 15:54
  • 1
    `and when user clicks on it, another very complex JPanel is expanded and showed,` - I don't understand the problem? It seems like you already have the event you need. And the code you posted is NOT a SSCCE, so I can't tell what you are actually doing on this click event since you attempt to create the panel when the frame is created. It doesn't show how you go from one tab to the other. All the "info" fields are not related to the problem since your question is about displaying the images. – camickr Feb 09 '18 at 16:22
  • I don't have the event I need. As you can see in my code, images in sud panel are resized and arranged the first time with a width of -1, simulating that the JPanel which should contain them is still not displayed. That's why you don't see them, which is the exact case I have in my software. They are not displayed because the JPanel into which they should be placed still does not exist, so they are given a negative width. Then, even if their panel if opened later, they won't display anyhow. – SagittariusA Feb 09 '18 at 16:27
  • 1
    As you like, there's nothing useful having to do with arrogant people. – SagittariusA Feb 09 '18 at 16:41
  • 1
    *"As you like.."* What I like is irrelevant. In fact, what you like is irrelevant. The important question is - do you want the help of others for free? If the answer is yes, it would pay to make it as easy to do so as possible. I note an edit, one that you obviously did not test. And a tip: Add @camickr (or whoever, the `@` is important) to *notify* the person of a new comment. But no need to notify me. I'm closing the browser tab with this question in it. Good luck with it. – Andrew Thompson Feb 09 '18 at 16:47
  • Still don't understand. Somehow an event is generated to display/expand the panel. At that time you do your calculations. `JPanel into which they should be placed still does not exist` - again, when you create that panel, you do your calculations. I don't understand your explanation of the problem and without a proper [MCVE] is don't understand the workflow, so I can't help. – camickr Feb 09 '18 at 16:58
  • And by the way, you don't even need images for your stated problem. Your problem is about the timing of invoking some logic. You can simulate the problem by just displaying a JLabel with some text. Or you could use a BufferedImage and just paint the image red/blue/green. The point of the `MCVE` is to demonstrate the concept of what you are doing. – camickr Feb 09 '18 at 17:02
  • My fault...I'm afraid I should not have asked this question because I cannot produce a MCVE from all those lines and classes. – SagittariusA Feb 09 '18 at 17:02
  • `when after accordion has opened, our clients click on a radio button and images are reloaded` - so this means you already have an ActionListener added to the radio button. Why can't you just invoke `button.setSelected(true)` in the logic after the accordion logic has fihished? Would this not be the same has the user clicking on the button? – camickr Feb 09 '18 at 17:07
  • Yes, it would be good but the problem is just I cannot understand where to call it. I mean I don't know when the accordion has finished loading and its panel is well displayed. The problem I was trying to explain is that formula for images calculates their size when the accordion is closed, so, as you can see from the image sample, it's container is very narrow. And it returns a negative number. Maybe it is bugged too my main problem is to get the point the JPanel finished displaying and then call `button.setSelected(true)` as you correctly say. – SagittariusA Feb 09 '18 at 17:14
  • Thank you for your kind help anyway. – SagittariusA Feb 09 '18 at 17:15

0 Answers0