Search code examples
javaswingwindow-resizewindowstate

Swing - windowStateChanged event trouble


I have a JFrame where some elements (one, for now) have to be centered manually on the contentPane when resizing the window or changing the window state. Resizing event (componentResized) works fine but the windowStateChanged event is causing problems because it doesn't seem to "update" the contentPane's new size properly - it keeps the previous value. This can be seen by simply printing the result of getSize called on contentPane.

Centering is done by programmatically changing the constraint of the component (using putConstraint, SpringLayout). The issue is that getWidth used in that method returns "wrong" values which results in an uncentered component. Maybe another listener is needed here?

Additional info: Eclipse with WindowBuilder, Linux Mint 15

Any kind of advice is appreciated.

addWindowStateListener(new WindowStateListener()
{
    public void windowStateChanged(WindowEvent e)
    {
        System.out.println(contentPane.getSize()); // returns old size
        tfArrayPanelCenter();
    }
});

public void tfArrayPanelCenter()
{
    int padding = (contentPane.getWidth()
            - tfArrayPanel.getPreferredSize().width - HGAP) / 2;
    sl_contentPane.putConstraint(SpringLayout.WEST, tfArrayPanel, padding,
            SpringLayout.WEST, contentPane);
}

As requested I'm posting more code - a simple game of hangman. I think the JFrame constructor should be enough (other stuff is non-GUI):

/**
 * Create the frame.
 */
public MainWindow()
{
    addWindowStateListener(new WindowStateListener()
    {
        // no "@Override" was generated but it is the same with it
        public void windowStateChanged(WindowEvent e)
        {
            System.out.println("EVENT: " + contentPane.getSize() + ", "
                    + getExtendedState() + ", " + e.getOldState()); // amusingly, states are actually correct - interchanging between 0 (Frame.NORMAL) and 6 (Frame.MAXIMIZED_BOTH) when I maximize and "unmaximize"
            tfArrayPanelCenter();
        }
    });
    addComponentListener(new ComponentAdapter()
    {
        @Override
        public void componentResized(ComponentEvent e)
        {
            tfArrayPanelCenter();
        }
    });
    setTitle("Hangman");
    setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    setBounds(100, 100, 790, 620);
    setLocationRelativeTo(null);
    GameMechanics.setMainWindow(this);
    contentPane = new JPanel();
    contentPane.setBorder(new EmptyBorder(5, 5, 5, 5));
    setContentPane(contentPane);
    sl_contentPane = new SpringLayout(); // I've declared this as a field, it is normally generated as a local variable
    contentPane.setLayout(sl_contentPane);

    newGame = new JButton("New Game");
    newGame.addMouseListener(new MouseAdapter()
    {
        @Override
        public void mouseClicked(MouseEvent e)
        {
            newGame.setVisible(false);
            lblGame.setVisible(true);
            lblGame.setBtnHintStatus(true);
            lblGame.setLblTriesLeftCountText(Integer
                    .toString(GameMechanics.TRIES));
            lblGame.paint(triesLeft);
            inputChars = new ArrayList<Character>();
            GameMechanics.loadWord();
            initControls(GameMechanics.getGuessingWord().length());
            lblTest.setText(GameMechanics.getGuessingWord().toUpperCase());
        }
    });
    newGame.setFont(new Font("Tempus Sans ITC", Font.BOLD, 12));
    sl_contentPane.putConstraint(SpringLayout.NORTH, newGame, 10,
            SpringLayout.NORTH, contentPane);
    sl_contentPane.putConstraint(SpringLayout.WEST, newGame, 10,
            SpringLayout.WEST, contentPane);
    newGame.setPreferredSize(new Dimension(100, 40));
    newGame.setFocusPainted(false);
    contentPane.add(newGame);

    tfArrayPanel = new JPanel();
    sl_contentPane.putConstraint(SpringLayout.SOUTH, tfArrayPanel, -10,
            SpringLayout.SOUTH, contentPane);
    sl_contentPane.putConstraint(SpringLayout.WEST, tfArrayPanel, 400,
            SpringLayout.WEST, contentPane);
    contentPane.add(tfArrayPanel);
    tfArrayPanel.setLayout(new FlowLayout(FlowLayout.CENTER, HGAP, VGAP));

    lblTest = new JLabel("New label");
    sl_contentPane.putConstraint(SpringLayout.NORTH, lblTest, 15,
            SpringLayout.NORTH, contentPane);
    sl_contentPane.putConstraint(SpringLayout.WEST, lblTest, 416,
            SpringLayout.WEST, contentPane);
    contentPane.add(lblTest);

    lblGame = new GameLabels(); // custom JPanel imported into the Palette and used from there on the MainWindow
    sl_contentPane.putConstraint(SpringLayout.SOUTH, lblGame, -60,
            SpringLayout.SOUTH, contentPane);
    sl_contentPane.putConstraint(SpringLayout.WEST, lblGame, 10,
            SpringLayout.WEST, contentPane);
    lblGame.setPreferredSize(new Dimension(315, 130));
    lblGame.setLayout(null);
    lblGame.setVisible(false);
    lblGame.setMainWindow(this);
    contentPane.add(lblGame);
}

Solution

  • Upon reading kleopatra's suggestion I approached the problem in another way. All I've done was putting the EAST constraint too (there were only WEST and SOUTH before) on the JPanel that needed to be centered. JPanel's FlowLayout handles the actual centering of the elements located within it.

    To be honest, this doesn't truly answer the question itself but it is a solution to my troubles regarding the centering of components. Good enough, I guess.