Java how to disable JPanel components automatic vertical resizing - java

I've ran into a pretty annoying problem. The JPanel is adding a vertical gap between components, and I need to get rid of that.
I'm trying to get this (Blue lines are the space I want to get rid of):
To look like this:
Here is my current class:
public class SummaryPanel extends JPanel
{
private JLabel bagelLabel;
private JLabel toppingLabel;
private JLabel coffeeLabel;
private JLabel shotsLabel;
private JLabel subtotal;
private JLabel tax;
private JLabel total;
private JPanel selectionsPanel;
private JPanel totalPanel;
public SummaryPanel()
{
bagelLabel = new JLabel("No bagel $0.00");
toppingLabel = new JLabel("No topping $0.00");
coffeeLabel = new JLabel("No coffee $0.00");
shotsLabel = new JLabel("(Includes 0 shots) $0.00");
subtotal = new JLabel("");
tax = new JLabel("");
total = new JLabel("");
setLayout(new GridLayout(2,1));
selectionsPanel = new JPanel();
selectionsPanel.setLayout(new GridLayout(4,1));
selectionsPanel.add(bagelLabel);
selectionsPanel.add(toppingLabel);
selectionsPanel.add(coffeeLabel );
selectionsPanel.add(shotsLabel );
totalPanel = new JPanel();
totalPanel.setLayout(new GridLayout(3,1));
totalPanel.add(subtotal);
totalPanel.add(tax);
totalPanel.add(total);
totalPanel.setVisible(false);
add(selectionsPanel);
add(totalPanel);
}
}

Its controlled by the layout manager.
setLayout(new GridLayout(2,1));
You are using a GridLayout so each of the two components gets the same space.
selectionsPanel.setLayout(new GridLayout(4,1));
In turn each JLabel get a quarter of the total space available to each panel.
Instead you could use a BorderLayout:
//setLayout(new GridLayout(2,1));
setLayout(new BorderLayout);
Then when you add components to the panel you use:
add(selectionsPanel, BorderLayout.PAGE_START);
add(totalsPanel, BorderLayout.PAGE_END);
Now the preferred sizes will be respected.

The GridLayout will divide the panel into the number of rows and columns specified, and each component will fill one of these cells in their entirety.
You may wish to consider using a BoxLayout instead. This will allow you to stack your components without them expanding unpleasantly.

Related

Extending a Panel Within a Panel Vertically

I am working on an application using the Swing GUI widget in Java and am having issues with stretching panels vertically within a panel. My main panel is using the BorderLayout and the problem is in the Center Region. I am using a Panel with a BoxLayout.X_AXIS and inside of it additional Panels with a BoxLayout.Y_AXIS are inserted. I am unable to stretch it vertically from top to bottom. I also want the contents to start from the top to the bottom. I have tried using the GridLayout and it does exactly what I want, however I am constrained width wise because all of the columns are equivalent size and I want some panels to have a smaller width. I have looked at the API, tutorials, and searched Googled for an answer. Any help would be great!
Attached are the code and the screenshot with the layouts.
import java.awt.*;
import javax.swing.*;
import javax.swing.border.EmptyBorder;
import java.util.*;
import java.awt.Color;
public class Colors
{
private JFrame frame;
private JPanel contentPane;
public static void main (String[] args){
Colors gui = new Colors();
gui.start();
}
public void start(){
frame = new JFrame("Words");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
contentPane = (JPanel)frame.getContentPane();
contentPane.setLayout(new BorderLayout(8,8));
center();
east();
frame.pack();
frame.setVisible(true);
}
private void center(){
JPanel centerPanel = new JPanel();
centerPanel.setLayout(new BoxLayout(centerPanel,BoxLayout.X_AXIS));
//centerPanel.setLayout(new GridLayout(1,3));
centerPanel.setPreferredSize(new Dimension(300,0));
centerPanel.setBackground(Color.BLUE);
JLabel oneLabel,twoLabel,threeLabel;
JPanel panelOne = new JPanel();
panelOne.setLayout(new BoxLayout(panelOne,BoxLayout.Y_AXIS));
panelOne.setBackground(Color.CYAN);
oneLabel = new JLabel(" First Label First Column ");
panelOne.add(oneLabel);
twoLabel = new JLabel(" Second Label First Column ");
panelOne.add(twoLabel);
threeLabel = new JLabel(" Third Label First Column ");
panelOne.add(threeLabel);
centerPanel.add(panelOne);
JPanel panelTwo = new JPanel();
panelTwo.setLayout(new BoxLayout(panelTwo,BoxLayout.Y_AXIS));
panelTwo.setBackground(Color.YELLOW);
oneLabel = new JLabel(" 10 ");
panelTwo.add(oneLabel);
twoLabel = new JLabel(" 20 ");
panelTwo.add(twoLabel);
threeLabel = new JLabel(" 30 ");
panelTwo.add(threeLabel);
centerPanel.add(panelTwo);
JPanel panelThree = new JPanel();
panelThree.setLayout(new BoxLayout(panelThree,BoxLayout.Y_AXIS));
panelThree.setBackground(Color.GREEN);
oneLabel = new JLabel(" 10 ");
panelThree.add(oneLabel);
twoLabel = new JLabel(" 20 ");
panelThree.add(twoLabel);
threeLabel = new JLabel(" 30 ");
panelThree.add(threeLabel);
centerPanel.add(panelThree);
contentPane.add(centerPanel, BorderLayout.CENTER);
}
private void east(){
JPanel panel = new JPanel();
panel.setLayout(new BoxLayout(panel,BoxLayout.Y_AXIS));
panel.setPreferredSize(new Dimension(100,150));
panel.setBackground(Color.RED);
JPanel labelPanel = new JPanel();
labelPanel.setBackground(Color.WHITE);
labelPanel.setMaximumSize(new Dimension(26,24));
panel.add(labelPanel);
labelPanel = new JPanel();
labelPanel.setBackground(Color.GREEN);
labelPanel.setMaximumSize(new Dimension(26,24));
panel.add(labelPanel);
contentPane.add(panel, BorderLayout.EAST);
}
}
I have tried using the GridLayout and it does exactly what I want, however I am constrained width wise because all of the columns are equivalent size and I want some panels to have a smaller width
You can try using the GridBagLayout. Each column will be the width of the largest component in the column.
Read the section from the Swing tutorial on How to Use GridBagLayout for more information and working examples.
Another option might be to use a JTable. A JTable allows you to display data in a row/column format. The above tutorial also has a section on How to Use Tables.

Which Swing layout manager to get my desired layout?

I am trying to make a basic login menu following this mock up :
I decided to put this whole menu into a JPanel so I can switch to another panel once the connexion is successful.
So I decided to use a Borderlayout to have the title in north area and the connect button in the south area .
I made the center of the borderlayout a panel itself . I decided to make it a gridlayout to both have the labels(login,password) but also the textfield in which the user will put his id.
The result is very ugly and very far from what I expected :
Here is the code of the menu :
public class EcranAccueil extends JPanel {
private JLabel labelTitre;
private JPanel PanelConnexion;
private JButton boutonConnexion;
private JLabel labelLogin;
private JLabel labelMotDepasse;
private JTextField loginUser;
private JTextField MotDepasseUser;
EcranAccueil(EcranGestion EcranPrincipale){
PanelConnexion = new JPanel();
this.setLayout(new BorderLayout());
PanelConnexion.setLayout(new GridLayout(2,2));
loginUser = new JTextField("User");
loginUser.setMinimumSize(new Dimension(20,20));
loginUser.setMaximumSize(new Dimension(20,20));
MotDepasseUser = new JTextField("Password");
boutonConnexion = new JButton("Connect");
boutonConnexion.setMinimumSize(new Dimension(200,200));
boutonConnexion.setMaximumSize(new Dimension(200,200));
labelTitre= new JLabel("ApplicationName");
labelLogin= new JLabel("Login");
labelMotDepasse = new JLabel("Password");
PanelConnexion.add(labelLogin);
PanelConnexion.add(loginUser);
PanelConnexion.add(labelMotDepasse);
PanelConnexion.add(MotDepasseUser);
this.add(labelTitre, BorderLayout.NORTH);
this.add(PanelConnexion, BorderLayout.CENTER);
this.add(boutonConnexion, BorderLayout.SOUTH);
} }
I tried to use a gridboxlayout but I completely failed at using it and it did not compile. Does anyone have advices or suggestion?
A common strategy to solve complex computing tasks, is to break them into small, well defined manageable tasks. Divide and conquer.
This also applies to gui: break the design into small, easy to layout containers.
In this case, for example start by dividing the design into 3 areas:
Each such area is implemented by a nested panel.
As you can see in the code, mainPanel is further divided into two nested panels, to ease and improve layout:
class EcranAccueil extends JPanel {
EcranAccueil(){
//Set layout (JPanel uses Flowlayout by default)
setLayout(new BorderLayout(5,5));
// a nested panel for application label
JPanel topPanel = new JPanel();
add(topPanel, BorderLayout.NORTH);
topPanel.setLayout(new FlowLayout(FlowLayout.LEADING));//set
JLabel labelTitre= new JLabel("ApplicationName");
topPanel.add(labelTitre);
// a nested panel for login and password, having two rows
JPanel mainPanel = new JPanel(new GridLayout(2, 1));
add(mainPanel, BorderLayout.CENTER);
JPanel loginPanel = new JPanel();
loginPanel.setLayout(new FlowLayout(FlowLayout.TRAILING));
mainPanel.add(loginPanel);
JLabel labelLogin = new JLabel("Login");
loginPanel.add(labelLogin);
JTextField loginUser = new JTextField("User");
loginUser.setColumns(10);
loginPanel.add(loginUser);
JPanel passwordPanel = new JPanel();
passwordPanel.setLayout(new FlowLayout(FlowLayout.TRAILING));
mainPanel.add(passwordPanel);
JLabel labelMotDepasse = new JLabel("Password");
passwordPanel.add(labelMotDepasse);
JTextField motDepasseUser = new JTextField("Password");
motDepasseUser.setColumns(10);
passwordPanel.add(motDepasseUser);
JPanel buttonPanel = new JPanel();
buttonPanel.setLayout(new FlowLayout(FlowLayout.CENTER));
add(buttonPanel,BorderLayout.SOUTH);
JButton boutonConnexion = new JButton("Connect");
buttonPanel.add(boutonConnexion);
}
}
Once you get the basic idea, the layout and its responsiveness can be further improved.
More examples of applying this strategy: 1 2 and 3

How do I use nested panels in my GUI application?

I created a drawing application that lets a user choose pen colors but I have having trouble with the layout. I created multiple panels but when I run it, all the buttons are still in one panel. Is there a way to fix this?
public class DrawingGUI extends JPanel {
private JRadioButton penColor1, penColor2, penColor3, randomPenColor, eraser;
private JButton clearButton;
private static Color defaultColor = Color.BLACK;
private static boolean isRandomSelected = false;
private final static int DIAMETER = 12;
protected static boolean canDraw;
private ArrayList<PointTracker> points;
public DrawingGUI() {
setBackground(Color.WHITE);
points = new ArrayList<PointTracker>();
JPanel drawPanel = new JPanel();
JLabel instructions = new JLabel("Enter your information:");
JPanel instructionsPanel = new JPanel();
instructionsPanel.add(instructions);
drawPanel.add(instructionsPanel);
JPanel colorPanel1 = new JPanel();
penColor1 = new JRadioButton("Red");
drawPanel.add(penColor1);
penColor1.addActionListener(new ToolListener());
drawPanel.add(colorPanel1);
JPanel colorPanel2 = new JPanel();
penColor2 = new JRadioButton("Blue");
drawPanel.add(penColor2);
penColor2.addActionListener(new ToolListener());
drawPanel.add(colorPanel2);
JPanel colorPanel3 = new JPanel();
penColor3 = new JRadioButton("Yellow");
drawPanel.add(penColor3);
penColor3.addActionListener(new ToolListener());
drawPanel.add(colorPanel3);...(So on)
all the buttons are still in one panel
Why is that a problem. That is what I would expect to happen.
Why are you creating a separate panel for each button? The whole point of using panel is to logically group components.
So I would expect you should have something like:
JPanel buttonsPanel = new JPanel();
buttonsPanel.add( button1 );
buttonsPanel.add( button2 );
buttonsPanel.add( button3 );
JPanel drawPanel = new JPanel();
drawPanel.add( component1 );
drawPanel.add( component2 );
frame.add(drawPanel, BorderLayout.PAGE_START);
frame.add(buttonsPanel, BorderLayout.PAGE_END);
Above is a simple example of "nesting" two panels on a frame. Each of the panel can use a different layout manager as required.
For a working example of this approach you can check out Custom Painting Approaches. Both code examples show how you can "nest" a drawing panel and a buttons panel in a frame.

Button layout outside of a GridLayout

I'm currently doing a quite simple GUI and was wondering how I could get the button in question out from the GridLayout and put it in its own say BorderLayout, if that's a bit vague I'll attach images to show you what I mean:
With that picture I would like the button to not be with the grid layout and for it to fill all the way across at the bottom of the program as it would in a border layout. My code is as follows:
import java.awt.*;
import javax.swing.*;
import java.awt.event.*;
/**
* Write a description of class HW4GUI here.
*
* #author (your name)
* #version (a version number or a date)
*/
public class HW4GUI extends JFrame implements ActionListener
{
private JButton jbtAction;
private JTextField jtfFName;
private JTextField jtfLName;
private JTextField jtfLibNo;
private int nextLibNo;
private JPanel textPanel;
/**
* The constructor for the GUI, also initalises nextLibNo number
*/
public HW4GUI()
{
super("Adding a borrower");
makeFrame();
showFrame();
nextLibNo = 1001;
}
/**
*
*/
private void makeFrame()
{
setLayout(new GridLayout(4,0));
setResizable(false);
textPanel = new JPanel();
//textPanel.setLayout(new FlowLayout(FlowLayout.CENTER));
textPanel.setLayout(new BorderLayout());
jtfFName = new JTextField(15);
JLabel fNLbl = new JLabel("First Name: ");
add(fNLbl);
add(jtfFName);
// add(textPanel);
fNLbl.setHorizontalAlignment(JLabel.RIGHT);
jtfFName.setEditable(true);
jtfLName = new JTextField(15);
JLabel lNLbl = new JLabel("Last Name: ");
add(lNLbl);
add(jtfLName);
//add(textPanel);
lNLbl.setHorizontalAlignment(JLabel.RIGHT);
jtfLName.setEditable(true);
jtfLibNo = new JTextField(15);
JLabel lNOLbl = new JLabel("Library Number: ");
add(lNOLbl);
add(jtfLibNo);
// add(textPanel);
lNOLbl.setHorizontalAlignment(JLabel.RIGHT);
jtfLibNo.setEditable(false);
jbtAction = new JButton("Add Borrower");
add(jbtAction, BorderLayout.SOUTH);
jbtAction.addActionListener(this);
}
/**
* displays the frame window where you can set the size of it and also other variables
*/
private void showFrame()
{
setSize(400,200);
setResizable(false);
setLocationRelativeTo( null);
setVisible(true);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
}
public void actionPerformed(ActionEvent e)
{
String fn = jtfFName.getText();
String ln = jtfLName.getText();
boolean valid = true;
if (e.getActionCommand().equals("Add Borrower"))
{
if (fn.equals("") && (ln.equals("")))
{
jtfLibNo.setText("No Names");
valid = false;
}
else if (fn.equals("") )
{
jtfLibNo.setText("No First Name");
valid = false;
}
else if (ln.equals(""))
{
jtfLibNo.setText("No Last Name");
valid = false;
}
else
if (valid == true)
{
String lib = Integer.toString(nextLibNo++);
jtfLibNo.setText(lib);
jbtAction.setText("Confirm");
}
}
if (e.getActionCommand().equals("Confirm"))
{
jtfLibNo.setText("");
jbtAction.setText("Add Borrower");
}
}
}
As you have said that you want the Button outside your GridLayout, you can do:
Declare a new Panel, like mainPanel or something like that.
JPanel mp = new JPanel();
Set its layout to 3x1 using GridLayout.
mp.setlayout(new GridLayout(3,1));
Add you labels and text-fields to that panel.
mp.add(fNLbl);// and the rest.
Add this panel to your frame.
add(mp, BorderLayout.CENTER);
Then add the Button at the end, using, BorderLayout.SOUTH.
add(jbtAction, BorderLayout.SOUTH);
But as far as my knowledge goes, then your button will occupy the width of the whole frame. So, instead, you can add the button to a panel, and then add that panel to it. Like:
add( new JPanel(){{ add(jbtAction);}}, BorderLayout.SOUTH); // this is double-brace initialization.
The following code works fine:
private void makeFrame()
{
JPanel mp = new JPanel();
mp.setLayout(new GridLayout(3,1));
setResizable(false);
textPanel = new JPanel();
//textPanel.setLayout(new FlowLayout(FlowLayout.CENTER));
textPanel.setLayout(new BorderLayout());
jtfFName = new JTextField(15);
JLabel fNLbl = new JLabel("First Name: ");
mp.add(fNLbl);
mp.add(jtfFName);
// add(textPanel);
fNLbl.setHorizontalAlignment(JLabel.RIGHT);
jtfFName.setEditable(true);
jtfLName = new JTextField(15);
JLabel lNLbl = new JLabel("Last Name: ");
mp.add(lNLbl);
mp.add(jtfLName);
//add(textPanel);
lNLbl.setHorizontalAlignment(JLabel.RIGHT);
jtfLName.setEditable(true);
jtfLibNo = new JTextField(15);
JLabel lNOLbl = new JLabel("Library Number: ");
mp.add(lNOLbl);
mp.add(jtfLibNo);
// add(textPanel);
lNOLbl.setHorizontalAlignment(JLabel.RIGHT);
jtfLibNo.setEditable(false);
jbtAction = new JButton("Add Borrower");
add(mp, BorderLayout.CENTER);
add( new JPanel(){{ add(jbtAction);}}, BorderLayout.SOUTH);
jbtAction.addActionListener(this);
}
With that picture I would like the button to not be with the grid layout and for it to fill all the way across at the bottom of the program as it would in a border layout
Then use a BorderLayout. The default layout manager for a JFrame is a BorderLayout. So you would do somethinglike:
Create a panel using a GridLayout. Add the first 5 components to this panel. Then add the panel to the "CENTER" of the frame.
Create your button. Add the button the the "PAGE_END" of the frame.
The idea of layout managers is that you can nest panels with different layouts to achieve your final layout.
I also agree, the main panel with multiple buttons should probably be a GridBagLayout as it will size each column to the width of the widest component in the column instead of making every column width identical, which will make the panel look better. Read the section from the Swing tutorial on How to Use GridBagLayout for more information and working examples.

How to use VerticalGlue in box layout

I'm trying to build a simple panel which I will throw some fields onto and capture some user data. I typically use a combination of GridBagLayouts (thanks to trashgod) and BoxLayouts to achieve the layout I want. I normally don't have any issues with using them and they just do what makes intuitive sense 99% of the time, but I can't seem to make this rather simple panel function properly. Can anyone tell me why?
The panel class:
import java.awt.Color;
import java.awt.Dimension;
import javax.swing.BorderFactory;
import javax.swing.Box;
import javax.swing.BoxLayout;
import javax.swing.JButton;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JTextField;
public class EmailPanel extends JPanel {
private JButton m_OkButton;
private JPanel m_MainPanel;
private JTextField m_ServerIPTF;
private JTextField m_ServerPortTF;
private JTextField m_DomainNameTF;
private JTextField m_UnitNameTF;
private JTextField m_Recipient1TF;
private JTextField m_Recipient2TF;
private final Dimension LARGE_TEXTFIELD_SIZE = new Dimension(125, 25);
public EmailPanel() {
init();
}
private void init() {
this.setLayout(new BoxLayout(this, BoxLayout.Y_AXIS));
JPanel tPanel;
JLabel tLabel;
Header: {
tPanel = new JPanel();
tPanel.setLayout(new BoxLayout(tPanel, BoxLayout.X_AXIS));
tPanel.add(Box.createHorizontalGlue());
tLabel = new JLabel("Email Settings");
tPanel.add(tLabel);
tPanel.add(Box.createHorizontalGlue());
tPanel.setBorder(BorderFactory.createMatteBorder(0, 0, 3, 0, Color.red));
this.add(tPanel);
}
MainPanel: {
m_MainPanel = new JPanel();
m_MainPanel.setLayout(new BoxLayout(m_MainPanel, BoxLayout.Y_AXIS));
m_MainPanel.add(Box.createVerticalStrut(5));
tPanel = new JPanel();
tPanel.setLayout(new BoxLayout(tPanel, BoxLayout.X_AXIS));
tPanel.add(Box.createHorizontalStrut(3));
tLabel = new JLabel("Server IP Address:");
tPanel.add(tLabel);
tPanel.add(Box.createHorizontalStrut(3));
m_ServerIPTF = new JTextField();
m_ServerIPTF.setMinimumSize(LARGE_TEXTFIELD_SIZE);
m_ServerIPTF.setMaximumSize(LARGE_TEXTFIELD_SIZE);
m_ServerIPTF.setPreferredSize(LARGE_TEXTFIELD_SIZE);
tPanel.add(m_ServerIPTF);
tPanel.add(Box.createHorizontalStrut(25));
tLabel = new JLabel("Server Port");
tPanel.add(tLabel);
tPanel.add(Box.createHorizontalStrut(3));
m_ServerPortTF = new JTextField();
m_ServerPortTF.setMinimumSize(LARGE_TEXTFIELD_SIZE);
m_ServerPortTF.setMaximumSize(LARGE_TEXTFIELD_SIZE);
m_ServerPortTF.setPreferredSize(LARGE_TEXTFIELD_SIZE);
tPanel.add(m_ServerPortTF);
tPanel.add(Box.createHorizontalGlue());
m_MainPanel.add(tPanel);
m_MainPanel.add(Box.createVerticalStrut(5));
tPanel = new JPanel();
tPanel.setLayout(new BoxLayout(tPanel, BoxLayout.X_AXIS));
tPanel.add(Box.createHorizontalStrut(6));
tLabel = new JLabel("Domain Name:");
tPanel.add(tLabel);
tPanel.add(Box.createHorizontalStrut(3));
m_DomainNameTF = new JTextField();
m_DomainNameTF.setMinimumSize(LARGE_TEXTFIELD_SIZE);
m_DomainNameTF.setMaximumSize(LARGE_TEXTFIELD_SIZE);
m_DomainNameTF.setPreferredSize(LARGE_TEXTFIELD_SIZE);
tPanel.add(m_DomainNameTF);
tPanel.add(Box.createHorizontalGlue());
m_MainPanel.add(tPanel);
this.add(m_MainPanel);
}
OKButton: {
m_OkButton = new JButton("Ok");
tPanel = new JPanel();
tPanel.setLayout(new BoxLayout(tPanel, BoxLayout.X_AXIS));
tPanel.add(Box.createHorizontalGlue());
tPanel.add(m_OkButton);
tPanel.add(Box.createHorizontalGlue());
this.add(tPanel);
}
this.add(Box.createVerticalGlue());
}
}
If you add this to / use this as a content pane, you will see that there are large gaps on the Y axis between the various controls. I'm under the impression that the vertical glue that I add at the end of the init method should grow to consume all the space below the OK button, and the controls would be pushed together as a consequence. What I'm seeing is that it seems to be splitting up the space evenly between the various instances of my temporary JPanel object tPanel and the vertical glue at the bottom. How do I make it stop doing that?
Edit: It seems that the behavior is the same both with and without the somewhat superfluous m_MainPanel object.
This is what I see when it renders and the form is made larger than needed for the controls. I would expect the vertical glue to fill the space below the OK button to keep the controls on the top of the form.
I copy-pasted your code and added the main method:
public static void main(String[] args) {
JFrame frame = new JFrame();
frame.getContentPane().add(new EmailPanel());
frame.pack();
frame.setVisible(true);
}
This is the result:
with or without the line this.add(Box.createVerticalGlue());
Is this what you wanted or not?
Edit: Solution
I edited your code to achieve the desired result:
public class EmailPanel extends JPanel {
private JButton okButton;
private JTextField serverIPTF;
private JTextField serverPortTF;
private JTextField domainNameTF;
public static void main(String[] args) {
JFrame frame = new JFrame();
frame.getContentPane().add(new EmailPanel());
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.pack();
frame.setMinimumSize(frame.getPreferredSize());
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
public EmailPanel() {
init();
}
private void init() {
setLayout(new BoxLayout(this, BoxLayout.Y_AXIS));
JPanel tPanel;
JLabel tLabel;
// Header
tLabel = new JLabel("Email Settings", JLabel.CENTER);
tLabel.setAlignmentX(CENTER_ALIGNMENT);
tLabel.setMaximumSize(new Dimension(Integer.MAX_VALUE, tLabel.getPreferredSize().height));
tLabel.setBorder(BorderFactory.createMatteBorder(0, 0, 3, 0, Color.red));
add(tLabel);
// Fields
JPanel fieldsPanel = new JPanel();
fieldsPanel.setLayout(new BoxLayout(fieldsPanel, BoxLayout.Y_AXIS));
fieldsPanel.setBorder(BorderFactory.createMatteBorder(5, 3, 5, 3, new Color(0, 0, 255, 255)));
// Top fields
serverIPTF = new JTextField(10);
serverIPTF.setMaximumSize(serverIPTF.getPreferredSize());
serverPortTF = new JTextField(10);
serverPortTF.setMaximumSize(serverPortTF.getPreferredSize());
tPanel = new JPanel();
tPanel.setLayout(new BoxLayout(tPanel, BoxLayout.X_AXIS));
tPanel.add(new JLabel("Server IP Address:"));
tPanel.add(Box.createRigidArea(new Dimension(3, 0)));
tPanel.add(serverIPTF);
tPanel.add(Box.createRigidArea(new Dimension(25, 0)));
tPanel.add(new JLabel("Server Port"));
tPanel.add(Box.createRigidArea(new Dimension(3, 0)));
tPanel.add(serverPortTF);
tPanel.add(Box.createHorizontalGlue());
fieldsPanel.add(tPanel);
fieldsPanel.add(Box.createRigidArea(new Dimension(0, 5)));
// Lower field
domainNameTF = new JTextField(10);
domainNameTF.setMaximumSize(domainNameTF.getPreferredSize());
tPanel = new JPanel();
tPanel.setLayout(new BoxLayout(tPanel, BoxLayout.X_AXIS));
tPanel.add(new JLabel("Domain Name:"));
tPanel.add(Box.createRigidArea(new Dimension(3, 0)));
tPanel.add(domainNameTF);
tPanel.add(Box.createHorizontalGlue());
fieldsPanel.add(tPanel);
add(fieldsPanel);
// OK Button
okButton = new JButton("OK");
okButton.setAlignmentX(CENTER_ALIGNMENT);
add(okButton);
}
}
Explanation:
BoxLayout says:
When a BoxLayout lays out components from top to bottom, it tries to size each component at the component's preferred height. If the vertical space of the layout does not match the sum of the preferred heights, then BoxLayout tries to resize the components to fill the space. The components either grow or shrink to fill the space, with BoxLayout honoring the minimum and maximum sizes of each of the components. Any extra space appears at the bottom of the container.
(emphasis mine)
Which tells us that if we restrict the components' maximum height to their preferred height, all the extra vertical space will go to the bottom, just as you want. Hence, we added for all the text fields (the labels do not grow vertically) the line:
nameTF.setMaximumSize(nameTF.getPreferredSize());
and we don't need any vertical glue.
Notes:
I created the text fields with 10 columns, you can change this value.
The top label does not need horizontal glue to stretch it, just relax the maximum width constraint and set the alignment (similarly to the bottom button).
Instead of creating a lot of rigid areas (you used struts), I created a border with the appropriate widths. It is blue for visual purposes, but you should set its alpha to 0 to make is transparent.
Use createRigidArea instead of createXXXStrut (see the note in the above link).
I used frame.setMinimumSize(frame.getPreferredSize()) to not let the window resize to a smaller size than its contents. This is optional.
Non-final fields and variables should not use underscore (_) in the name according to Java naming conventions.
You did not specify horizontal stretching behavior, so it does whatever it does.
I still think that box layout is not the best approach here, or at least do not allow resizing of the window at all (so to not deal with extra space).

Categories