2

I have a JDialog and I want to have it a certain, given size:

JDialog dialog = new JDialog();
dialog.setSize(800, 600);
dialog.setResizable(false);

Then I add a component:

JLabel label = new JLabel("Test");
dialog.add(label);

Now I could make the dialog visible and check the size of the component

dialog.setVisible(true);
System.out.println(label.getSize());

The answer would be "[width=784,height=562]". Obviously the component was resized to fill the whole client area / content pane of the dialog window. That's fine and as I want it.

Question: How can I obtain the final size of the components before calling setVisible(true)?

  • getPreferredSize() will not be the size I want because I want the component to adapt to the given dialog's size
  • dialog.pack() is also not the right thing because it resizes the dialog to the preferred size of the components which I don't want
  • dialog.validate() does nothing useful here, the size of the component is still 0
  • dialog.getLayout().layoutContainer(dialog) also does not set the size of the components

So I am at a loss here. I want to make the layoutmanager calculating the right sizes of all components and sub components before showing the dialog, adapted to the overall size of the dialog. But I don't know how.

NoDataDumpNoContribution
  • 10,591
  • 9
  • 64
  • 104
  • In my application the Component is not a Label but a JPanel, more specific a geographical map, whose size should adapt to the dialogs area. This map wants to know it's size during initialization phase in order to know how many tiles are displayed and so on. It could have been done also later during the first paintComponent call, but I think it is cleaner this way. Is it possible and how or why not? – NoDataDumpNoContribution Oct 24 '12 at 09:44
  • sounds like a hen-and-egg problem :-) You need some additional logic to decide about the initial size, like f.i. ask the map for its prefSize given a number of tiles, and/or the relative area of screen you want your dialog to fill. Details hard to tell without seeing any code. – kleopatra Oct 24 '12 at 09:51
  • The initial size is given from outside (800x600) and setVisible(true) arrives at the wanted size of the component without any additional information. Most probably I just want to do the same but without the displaying part. I just want to layout all my components with the constraint that the dialog's size is known. The map component has no preferred size as such but takes anything it can get, fills the area, which the default layout manager of content pane does. I think the code above is complete for describing the problem. – NoDataDumpNoContribution Oct 24 '12 at 09:57
  • 1
    no, you *don't* want the end-size for the layout - if you feel like needing it, something is wrong in your setup/expectation. – kleopatra Oct 24 '12 at 10:02
  • At least after setting the dialog visible one knows the end-size of the layout. Maybe I can find a workaround or even better solution, but first I want to know if it is possible like this? The question has value as it is. – NoDataDumpNoContribution Oct 24 '12 at 10:10
  • -1 for insisting on a very probably wrong approach without showing the actual requirement ... – kleopatra Oct 24 '12 at 10:13
  • I don't mind the -1 although I think the question has value in itself, the code example is complete and you are not answering the question. The requirements are indicated in the question: dialog has given size, component should adapt to it. All there is. – NoDataDumpNoContribution Oct 24 '12 at 10:16
  • Please edit your question to include an [sscce](http://sscce.org/) that demonstrates your approach. This related [example](http://stackoverflow.com/a/9118753/230513) may be helpful. The geometry should be correct after `pack()` and before `setVisible()`. – trashgod Oct 24 '12 at 17:15
  • I don't know what to add. The 7 code lines above are sscce. Just execute them in any static function and you get an output on the standard output. Now try to come to the same result, i.e. the same size of the component, without making the dialog visible. I can confirm that the geometry is not the same after pack() and after setVisible(). It's different as it should be. I think the question has been misunderstood so far but I can now try to post my version of an answer. – NoDataDumpNoContribution Oct 25 '12 at 06:04

2 Answers2

3

I now found that it can be done as follows:

JDialog dialog = new JDialog();
JLabel label = new JLabel("Test");
dialog.add(label);

// pack(), setSize(), validate() in this order will
// set sizes on all components as wished
dialog.pack();
dialog.setSize(800, 600);
dialog.validate();

System.out.println(label.getSize());

Also here the output is "[width=784,height=562]" but the dialog is not yet visible. The important part is the combination of pack(), setSize(desiredSize) and validate() in this order. The pack() probably determines a new size of the dialog (preferred sizes of all components), that's why here the size has to be set afterwards and the validate() is responsible for the resizing of the components. Probably setVisible(true) which arrives at the same sizes is doing internally something similar.

It seems a bit of a waste to resize the components several times but without pack() also setSize() and validate() do not have any effect.

I guess the other answers were based on some misunderstanding because they implicitly always assumed that you want to have the preferred size, but there are cases, e.g. if the user resizes the dialog or if the dialogs size is fixed from the beginning where you cannot attain the preferred size and some components just have to fill the available space.

That was the layout problem here, having a given global size of the dialog and determining the size of the components as they fill the available space. LayoutManagers solve this problem quite nicely, however usually only after setVisible(true).

I tested a bit more:

    // new dialog
    JDialog dialog = new JDialog();

    // new label, prints messages if resized or painted
    JLabel label = new JLabel("Test") {
        @Override
        protected void paintComponent(Graphics g) {
            super.paintComponent(g);
            System.out.println("Component painted.");
        }
    };
    label.addComponentListener(new ComponentAdapter() {
        @Override
        public void componentResized(ComponentEvent e) {
            System.out.println("Resized: " + e.getComponent().getSize());
        }
    });
    dialog.add(label);        
    System.out.println("Size after new JLabel: " + label.getSize());

    // pack dialog - necessary for setSize/validate to work
    dialog.pack();
    System.out.println("Size after pack: " + label.getSize());                

    // set a size and validate changes sizes
    dialog.setSize(800, 600);
    dialog.validate();
    System.out.println("Size after setSize and validate: " + label.getSize());        

    // set visible would have also done the trick
    dialog.setVisible(true);
    System.out.println("Size after setVisible(true): " + label.getSize());

    // and another resizing (no validation neccessary)
    dialog.setSize(300, 200);

    // dispose
    dialog.dispose();

And the output is

  • Size after new JLabel:java.awt.Dimension[width=0,height=0]
  • Size after pack: java.awt.Dimension[width=116,height=16]
  • Size after setSize and validate: java.awt.Dimension[width=784,height=562]
  • Size after setVisible(true): java.awt.Dimension[width=784,height=562]
  • Resized: java.awt.Dimension[width=284,height=162]
  • Resized: java.awt.Dimension[width=284,height=162]
  • Component painted.

I learned more about the inner workings of Swing:

  • ComponentResized events are not fired before setVisible(true) even if components are resized (their size changes)
  • ComponentResized events even with the same size can be fired several times in a row
  • Components might not be painted in between resizing if they follow each other fast enough
  • The first painting is in any case after setVisible(true) and the component will have the desired size (preferred size or defined by other constraints as here) by then.
  • If for some reason you must know the size of the components before the first drawing, do it with pack(), setSize(), validate()

I tested some more, also with maximized frames and now can combine all the results into: The first painComponent() is always with the right size and the related componentResized() event always follows afterwards, sometimes two times.However the LayoutManager must know before, otherwise the examples would not be drawn correctly. So in case one draws the background by itself, either read out the right size in every paintComponent or implement a custom layout manager or wait for the resized event and invoke repaint, so the component is drawn two times but it should work. Apllications include cases where the number of components to show depend on the size (as in my geographical map application).


Just to complete the picture I think the flow goes like this in case a user maximized or resized a frame/dialog:

  • frame/dialog.setSize()
  • LayoutManager.layoutContainer(frame/dialog) using the actual size
  • frame/dialog paint() using the layouted sizes
  • Resized() events fired for all components etc.

And pack() probably just calls setSize(layout.preferredLayoutSize()) as the first step.

So in case depending on the size you have to add or remove components for example, it could be a good idea to override setSize() and listen there for changes. I initially was listening for Resized() events but they arrive too late for the first drawing.

NoDataDumpNoContribution
  • 10,591
  • 9
  • 64
  • 104
2

Top-Level Containers return own Size, PreferredSize, Bounds in two cases (if they are)

  • already visible

  • after call pack(), in your case dialog.pack();

  • have to calculating with Borders and ToolBar came from Native OS

  • have to get this size from Top-Level Containers#getContentPane().getWhatever

  • most of JComponents returns own PreferredSize, then there no reason to sizing for Standard Layout Manager

mKorbel
  • 109,525
  • 20
  • 134
  • 319
  • Your answer suggests, the task from above is not possible without setting the dialog visible, right? For example with dialog.pack() and label.getSize() the size of the label is "[width=126,height=16]", the preferred size of the label, not the same as after setting visible. – NoDataDumpNoContribution Oct 24 '12 at 09:49
  • is possible, in all cases you can to setPreferredSize, then to call pack(), no issue, but I described about [JTextField("some desc",15)](http://docs.oracle.com/javase/7/docs/api/javax/swing/JTextField.html#JTextField%28java.lang.String,%20int%29), or [JTextArea(10, 15)](http://docs.oracle.com/javase/7/docs/api/javax/swing/JTextArea.html#JTextArea%28java.lang.String,%20int,%20int%29), e.i. – mKorbel Oct 24 '12 at 09:55
  • _the task from above is not possible without setting the dialog visible_ no, that's just @mKorbel's humour ;-) you don't want the actual size, you want the preferredSize. Your map should return something useful ... – kleopatra Oct 24 '12 at 09:57
  • JDialog (JFrame) has implemented BorderLayout, then after call pack() is shrinked to the graphics value in pixels, returned from String value in the JLabel – mKorbel Oct 24 '12 at 09:57
  • The component has no meaningful preferred size. The only meaningful value is the total size of the dialog and the component is supposed to adapt to this which seems like a well-defined layout problem. Setvisible(true) does the job but does to much. Question is if there is an alternative to it. – NoDataDumpNoContribution Oct 24 '12 at 10:01