3

After manually swapping components via add and remove, I invoke validate() on the container. According to the documentation,

The validate method is used to cause a container to lay out its subcomponents again. It should be invoked when this container's subcomponents are modified (added to or removed from the container, or layout-related information changed) after the container has been displayed.

The phrase "lay out its subcomponents again" makes me think that the container will resize itself accordingly, but it doesn't. Instead, after invoking validate(), I need to invoke pack() as well in order to view all its subcomponents.

Why is this? Am I doing something wrong?

mre
  • 43,520
  • 33
  • 120
  • 170
  • When using Swing I use `revalidate()`. Again, it will not resize the frame, but it will resize the Container, which may cause the layout to change. For example if the container is in a scrollpane, then you may see scrollbars added or removed. – camickr Jan 05 '12 at 16:20
  • @camickr - might be misunderstanding what you are saying, but a re/validate on a container will never resize that container, only its children – kleopatra Jan 06 '12 at 10:34
  • @kleopatra, looking at my comment again, I guess I should have not use the term "Container". I was using the term "container" to mean "jpanel" and was trying to emphasize that it would only affect the layout of the components within the "container". It will not resize a top level container (JFrame, JDialog, JWindow). – camickr Jan 06 '12 at 16:13
  • @camickr good point! I missed the validation root :-) Actually, I assumed you meant _container_ the way you explained in your edited comment :-) – kleopatra Jan 06 '12 at 16:22

3 Answers3

7

I think that you answered your question by yourself, hope help you this demo

import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import javax.swing.border.LineBorder;

public class AddComponentsAtRuntime {

    private JFrame f;
    private JPanel panel;
    private JCheckBox checkValidate, checkReValidate, checkRepaint, checkPack;

    public AddComponentsAtRuntime() {
        JButton b = new JButton();
        b.setBackground(Color.red);
        b.setBorder(new LineBorder(Color.black, 2));
        b.setPreferredSize(new Dimension(600, 10));
        panel = new JPanel(new GridLayout(0, 1));
        panel.add(b);
        f = new JFrame();
        f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        f.add(panel, "Center");
        f.add(getCheckBoxPanel(), "South");
        f.setLocation(200, 200);
        f.pack();
        f.setVisible(true);
    }

    private JPanel getCheckBoxPanel() {
        checkValidate = new JCheckBox("validate");
        checkValidate.setSelected(false);
        checkReValidate = new JCheckBox("revalidate");
        checkReValidate.setSelected(false);
        checkRepaint = new JCheckBox("repaint");
        checkRepaint.setSelected(false);
        checkPack = new JCheckBox("pack");
        checkPack.setSelected(false);
        JButton addComp = new JButton("Add New One");
        addComp.addActionListener(new ActionListener() {

            @Override
            public void actionPerformed(ActionEvent e) {
                JButton b = new JButton();
                b.setBackground(Color.red);
                b.setBorder(new LineBorder(Color.black, 2));
                b.setPreferredSize(new Dimension(600, 10));
                panel.add(b);
                makeChange();
                System.out.println(" Components Count after Adds :" + panel.getComponentCount());
            }
        });
        JButton removeComp = new JButton("Remove One");
        removeComp.addActionListener(new ActionListener() {

            @Override
            public void actionPerformed(ActionEvent e) {
                int count = panel.getComponentCount();
                if (count > 0) {
                    panel.remove(0);
                }
                makeChange();
                System.out.println(" Components Count after Removes :" + panel.getComponentCount());
            }
        });
        JPanel panel2 = new JPanel();
        panel2.add(checkValidate);
        panel2.add(checkReValidate);
        panel2.add(checkRepaint);
        panel2.add(checkPack);
        panel2.add(addComp);
        panel2.add(removeComp);
        return panel2;
    }

    private void makeChange() {
        if (checkValidate.isSelected()) {
            panel.validate();
        }
        if (checkReValidate.isSelected()) {
            panel.revalidate();
        }
        if (checkRepaint.isSelected()) {
            panel.repaint();
        }
        if (checkPack.isSelected()) {
            f.pack();
        }
    }

    public static void main(String[] args) {
        AddComponentsAtRuntime makingChanges = new AddComponentsAtRuntime();
    }
}
mKorbel
  • 109,525
  • 20
  • 134
  • 319
6

(may be due this ambiguity the description is changed in latest javaDoc )

JavaDoc 7 is NOT saying,

The validate method is used to cause a container to lay out its subcomponents again..

so its only laying the components, whereas you need a pack() again.

Note that pack() clearly says,

Causes this Window to be sized to fit the preferred size and layouts of its subcomponents.

COD3BOY
  • 11,964
  • 1
  • 38
  • 56
2

The fundamental, yet subtle assumption at play here: layout and size are directly related, 1-to-1. This is not the case, and is a common assumption in Swing programming. Size is the result of layout and size constraints.

Layout is:

  • Within the space constraints you've specified
  • And given the components I have to fit within that space
  • Position those components in relation to one another given the specified strategy (BoxLayout, BorderLayout, etc.)

If the LayoutManager can fit the components you've given to it, without changing the overall size of the container, it will not alter the size of the container. A call to pack, on the other hand, is an explicit request to minimize the space being used. That's the basic reason you're seeing the results that you are.

Some things you might try:

  • Make sure you're setting a maximum size on your components/containers, which will force size constraints on components when re-doing the layout
  • Always call pack() as a habit
  • Try some of the suggestions regarding common layout issues

It's tricky with Swing, because you've got to understand the painting pipeline, the layout managers, and some details of the windowing system. When it comes to the Swing documentation (and all the methods and the several different ways there are to doing any one thing) I try to read the documentation with an "assume nothing" approach, meaning, "What's the minimum possible thing that this method's documentation implies that it might do," and unless you observe additional behavior, don't get tricked into thinking that it does more than that.

Finally, I would add that the job of LayoutManagers in general is not sizing of containers so much as it is to place components in some relation to one another, according to the layout strategy (this is discussed in additional detail here). The idea is that, with the proper LayoutManager, you specify a basic layout strategy, and as a result when you resize the window they LayoutManager will intelligently move the components around so that your UI continues to follow that overall strategy. In this way layouts are basically meant to be independent of the overall size of the space in which they work, so they try not to make assumptions about what space is available - instead they take the size they are given and try to do what makes sense. Unless you explicitly put size constraints on your components, you can't guarantee what size they will be.

That means, if the LayoutManager doesn't believe that it needs to resize something in order to make it fit its overall strategy, basically it won't resize it. A call to pack, on the other hand, is an explicit request to pack things together and remove extra space.

jefflunt
  • 33,527
  • 7
  • 88
  • 126
  • 1
    the subtle incorrect assumption in this description (as I understand it, edit to clarify if I'm wrong in my understanding) is that a LayoutManager is sizing the _container_ it is mananging. It never does, instead it is completely focused on the _children_ of the container. At the toplevel container, the only methods that do any sizing are pack and setSize - one of which has to be invoked by the application code if appropriate. – kleopatra Jan 06 '12 at 10:32