11

I'm facing a problem with using BoxLayout.

In my example, I try to decrease the height of the text field and change the width of the buttons (as shown in green marker in the picture at the bottom). I know about the techniques setPreferredSize() and setMaximumSize(), but it did not work as it should. The line add(Box.createHorizontalGlue()) also did not help.

Thanks for any ideas.


public class Testy extends JPanel {
    public static void main(String[] args) {
        SwingUtilities.invokeLater(new Runnable() {
            @Override
            public void run() {
                constructGUI();
            }
        });
    }

    private static void constructGUI() {
        JFrame frame = new JFrame("Testy");
        frame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);

        JPanel centerPanel = new JPanel();
        centerPanel.setBackground(Color.DARK_GRAY);
        centerPanel.setPreferredSize(new Dimension(100, 400));
        frame.add(centerPanel, BorderLayout.CENTER);

        Testy eastPanel = new Testy();
        frame.add(eastPanel, BorderLayout.EAST);

        frame.pack();
        frame.setVisible(true);
    }

    public Testy() {
        setLayout(new BoxLayout(this, BoxLayout.PAGE_AXIS));

        JButton button = new JButton("Button ...... 1");
        //button.setPreferredSize(...);
        //button.setMaximumSize(...);
        add(button);

        button = new JButton("Button 2");
        //button.setPreferredSize(...);
        //button.setMaximumSize(...);
        add(button);

        button = new JButton("Button ........... 3");
        //button.setPreferredSize(...);
        //button.setMaximumSize(...);
        add(button);

        JLabel label = new JLabel("Label");
        //label.setPreferredSize(...);
        //label.setMaximumSize(...);
        add(label);

        JTextField textField = new JTextField();
        //textField.setPreferredSize(...);
        //textField.setMaximumSize(...);
        add(textField);

        button = new JButton("Button 4");
        //button.setPreferredSize(...);
        //button.setMaximumSize(...);
        add(button);

        //add(Box.createHorizontalGlue());
    }
}

picture

TT.
  • 15,774
  • 6
  • 47
  • 88
Dumas45
  • 501
  • 1
  • 10
  • 20
  • 2
    As a quick remedy, you can use nested layouts, in the sense, that on the right side, create a `JPanel` with `BorderLayout`, put a `JPanel(say centerPanel)` at the `CENTER` and a `JPanel(say buttonPanel)` at `PAGE_END` location. Now use a new `JPanel(say compPanel)` with `GridLayout` and put all the components on it, and place this `compPanel` inside `centerPanel`. Place `JButton(button4)` inside `buttonPanel` as is. I guess this will work :-) – nIcE cOw Aug 23 '13 at 15:56
  • 2
    For better help sooner, post an [SSCCE](http://sscce.org/). An SSCCE should include imports, a class definition & a `main` method. – Andrew Thompson Aug 23 '13 at 18:07

3 Answers3

30

First you have to realize that component position and size in Java Swing depends on Layout manager (if layout manager is set) not on the component itself. The component requests the manager for size.

For this case I would use different layout - combination of GridLayout and BorderLayout is enough and very simple and straightforward. But if want use BoxLayout, then...

  1. Documentation says:

    BoxLayout pays attention to a component's requested minimum, preferred, and maximum sizes. While you are fine-tuning the layout, you might need to adjust these sizes. ... For example, a button's maximum size is generally the same as its preferred size. If you want the button to be drawn wider when additional space is available, then you need to change its maximum size.

  2. Then set components maximum size: c.setMaximumSize(new Dimension(Integer.MAX_VALUE, c.getMinimumSize().height)); (c means button, label and textField in your example)

Edit 1:

Here is working source code:

import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;

import javax.swing.BoxLayout;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JTextField;
import javax.swing.SwingUtilities;
import javax.swing.WindowConstants;

public class Testy extends JPanel {
    public static void main(String[] args) {
        SwingUtilities.invokeLater(new Runnable() {
            public void run() {
                constructGUI();
            }
        });
    }

    private static void constructGUI() {
        JFrame frame = new JFrame("Testy");
        frame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);

        JPanel centerPanel = new JPanel();
        centerPanel.setBackground(Color.DARK_GRAY);
        centerPanel.setPreferredSize(new Dimension(100, 400));
        frame.add(centerPanel, BorderLayout.CENTER);

        Testy eastPanel = new Testy();
        frame.add(eastPanel, BorderLayout.EAST);

        frame.pack();
        frame.setVisible(true);
    }

    public Testy() {
        setLayout(new BoxLayout(this, BoxLayout.PAGE_AXIS));

        JButton button = new JButton("Button ...... 1");
        //button.setPreferredSize(...);
        button.setMaximumSize(new Dimension(Integer.MAX_VALUE, button.getMinimumSize().height));
        add(button);

        button = new JButton("Button 2");
        //button.setPreferredSize(...);
        button.setMaximumSize(new Dimension(Integer.MAX_VALUE, button.getMinimumSize().height));
        add(button);

        button = new JButton("Button ........... 3");
        //button.setPreferredSize(...);
        button.setMaximumSize(new Dimension(Integer.MAX_VALUE, button.getMinimumSize().height));
        add(button);

        JLabel label = new JLabel("Label");
        //label.setPreferredSize(...);
        label.setMaximumSize(new Dimension(Integer.MAX_VALUE, label.getMinimumSize().height));
        add(label);

        JTextField textField = new JTextField();
        //textField.setPreferredSize(...);
        textField.setMaximumSize(new Dimension(Integer.MAX_VALUE, textField.getMinimumSize().height));
        add(textField);

        button = new JButton("Button 4");
        //button.setPreferredSize(...);
        button.setMaximumSize(new Dimension(Integer.MAX_VALUE, button.getMinimumSize().height));
        add(button);

        // add(Box.createVerticalGlue());
    }
}

Screenshot

Edit 2:

If you want laid out Button 4 at the bottom of right column add this line add(Box.createVerticalGlue()); between add(textField); and button = new JButton("Button 4");.

Boris Šuška
  • 1,796
  • 20
  • 32
  • I liked this update, though already upvoted it, much before, to negate the downvote done by someone else, since no explanation was given regarding the same. – nIcE cOw Aug 23 '13 at 16:21
  • I definitely need a box layout, and your example works as it should. Thanks for the clarification – Dumas45 Aug 24 '13 at 05:09
  • @Dumas45 _I definitely need a box layout_ - why? BTW, -1 for [setXXSize, which is a no-no-ever](http://stackoverflow.com/a/7229519/203657) – kleopatra Aug 24 '13 at 10:38
  • @kleopatra layout manager works with XXSizes or not. It depends on layout manger implementation. If you use BoxLayout, than you have to use setXXSize (or show me example without). If maxSize is set it will be used for BoxLayout. JButton sets maxSize to prefferedSize by default, then I have to change it to MAX_VALUE if I want expand button to available area. Height of button is set to some constant value (minSize.height) because I don't want expand it to available space. – Boris Šuška Aug 25 '13 at 12:02
  • _JButton sets maxSize to prefferedSize by default_ that's not true: it _returns_ its pref as max (vs. _sets_). The difference may appear small, but has far reaching consequences: the former is dynamic, the latter is static and hard-codes the _current_ pref height. This will be the wrong the moment the pref height changes, f.i. by setting a different font. The only safe way to tweak the xxSizes is to subclass and override the getters. Or: use a better LayoutManager that allows the fine-tuning on its own level ;-) – kleopatra Aug 25 '13 at 12:24
  • Thank you for clarification. Example with sublass I found [here](http://docs.oracle.com/javase/tutorial/uiswing/layout/box.html). It is much more work. – Boris Šuška Aug 27 '13 at 08:08
4

As a quick remedy, you can use nested layouts, in the sense, that on the right side, create a JPanel with BorderLayout, put a JPanel(say compPanel) at the CENTER and a JPanel(say buttonPanel) at PAGE_END location. Now use a new JPanel(say panel) with GridLayout and put all the components on it, and place this compPanel inside centerPanel. Place JButton(button4) inside buttonPanel as is.

BoxLayout on the contrary, respects the preferred size of a given JComponent, which is usually calculated based on the content the JComponent holds or given explicity, hence components do not tend to align well with respect to other given components.

Here is the working example :

import java.awt.*;
import javax.swing.*;

public class Testy extends JPanel {        

    private JPanel panel;
    private JPanel buttonPanel;

    public Testy() {
        setLayout(new BorderLayout(5, 5));

        JPanel compPanel = new JPanel();
        panel = new JPanel(new GridLayout(6, 1, 5, 5));     
        JButton button = new JButton("Button ...... 1");
        //button.setPreferredSize(...);
        //button.setMaximumSize(...);
        panel.add(button);

        button = new JButton("Button 2");
        //button.setPreferredSize(...);
        //button.setMaximumSize(...);
        panel.add(button);

        button = new JButton("Button ........... 3");
        //button.setPreferredSize(...);
        //button.setMaximumSize(...);
        panel.add(button);

        JLabel label = new JLabel("Label");
        //label.setPreferredSize(...);
        //label.setMaximumSize(...);
        panel.add(label);

        JTextField textField = new JTextField();
        //textField.setPreferredSize(...);
        //textField.setMaximumSize(...);
        panel.add(textField);
        compPanel.add(panel);

        buttonPanel = new JPanel();
        button = new JButton("Button 4");
        buttonPanel.add(button);

        add(compPanel, BorderLayout.CENTER);
        add(buttonPanel, BorderLayout.PAGE_END);
    }

    private void constructGUI() {
        JFrame frame = new JFrame("Testy");
        frame.setDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE);

        JPanel centerPanel = new JPanel();
        frame.getContentPane().setLayout(new BorderLayout(5, 5));
        centerPanel.setBackground(Color.DARK_GRAY);
        frame.add(centerPanel, BorderLayout.CENTER);

        frame.add(this, BorderLayout.LINE_END);

        frame.pack();
        frame.setVisible(true);
    }

    public static void main(String[] args) {
        SwingUtilities.invokeLater(new Runnable() {
            @Override
            public void run() {
                new Testy().constructGUI();
            }
        });
    }
}

OUTPUT :

LAYOUT EXAMPLE

nIcE cOw
  • 24,468
  • 7
  • 50
  • 143
1

This should get close, based on your draw, just need to work on that component below the JLabel (using setPreferredSize()):

JPanel main = new JPanel(new GridLayout(1, 2));

JPanel left = new JPanel();
//left.setPreferredSize(some size);
JPanel right = new JPanel(new GridLayout(6, 1));
//right.setPreferredSize(some size);

right.add(new JButton("Button 1"));
//...
right.add(new JButton("Button 4"));

main.add(left);
main.add(right);
Fernando
  • 7,785
  • 6
  • 49
  • 81
  • 2
    It is better to use `new GridLayout(0, 1)`, it means any number of rows but exactly one column - it has better meaning. Anyway I don't know what happens when I add more than 6 components. – Boris Šuška Aug 23 '13 at 16:35