How to set component size inside container with BoxLayout
Asked Answered
A

3

13

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

Arawak answered 23/8, 2013 at 14:44 Comment(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 :-)Depart
For better help sooner, post an SSCCE. An SSCCE should include imports, a class definition & a main method.Nameplate
B
33

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");.

Brucite answered 23/8, 2013 at 16:2 Comment(6)
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.Depart
I definitely need a box layout, and your example works as it should. Thanks for the clarificationArawak
@Arawak I definitely need a box layout - why? BTW, -1 for setXXSize, which is a no-no-everMoyna
@Moyna 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.Explicit
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 ;-)Moyna
Thank you for clarification. Example with sublass I found here. It is much more work.Explicit
D
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

Depart answered 23/8, 2013 at 16:10 Comment(1)
+1 This is the best solution if you don't need to use the BoxLayout.Explicit
R
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);
Retardment answered 23/8, 2013 at 16:9 Comment(1)
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.Explicit

© 2022 - 2024 — McMap. All rights reserved.