How do I use GroupLayout properly to move components/panels? - java

First off, I apologize for the long post, I just wanted to be clear and show you my issue so it can better be resolved.
I have the following code:
JPanel panelCard = new JPanel();
String[] cards = {"VISA", "MASTERCARD", "DISCOVER"};
JComboBox cardType = new JComboBox(cards);
panelCard.add(cardType);
GroupLayout layout2 = new GroupLayout(panelCCInfo);
panelCCInfo.setLayout(layout2);
layout2.setAutoCreateGaps(true);
layout2.setAutoCreateContainerGaps(true);
GroupLayout.SequentialGroup hGroup2 = layout2.createSequentialGroup();
hGroup2.addGroup(layout2.createParallelGroup()
.addComponent(cardName)
.addComponent(cardNumber)
.addComponent(expDate));
hGroup2.addGroup(layout2.createParallelGroup()
.addComponent(cardNameField)
.addComponent(cardNumberField)
.addComponent(expDateField));
hGroup2.addGroup(layout2.createParallelGroup()
.addComponent(panelCard));
layout2.setHorizontalGroup(hGroup2);
GroupLayout.SequentialGroup vGroup2 = layout2.createSequentialGroup();
vGroup2.addGroup(layout2.createParallelGroup(Alignment.BASELINE)
.addComponent(cardName)
.addComponent(cardNameField));
vGroup2.addGroup(layout2.createParallelGroup(Alignment.BASELINE)
.addComponent(cardNumber)
.addComponent(cardNumberField));
vGroup2.addGroup(layout2.createParallelGroup(Alignment.BASELINE)
.addComponent(expDate)
.addComponent(expDateField)
.addComponent(panelCard));
layout2.setVerticalGroup(vGroup2);
panelCheckout.add(panelCCInfo, BorderLayout.CENTER);
And when I run it, it displays this window:
But I would like to achieve this:
The problem occurs when I try to add a 3rd vertical group with the panelCard in
the code:
hGroup2.addGroup(layout2.createParallelGroup().addComponent(panelCard));
It would seem that the 2 text field are cut off because of this, but I dont want it cut off.
What can I do to achieve the 2nd picture?
Thanks so much!

Here is how I'd do it:
import javax.swing.*;
import static javax.swing.GroupLayout.*;
import static javax.swing.GroupLayout.Alignment.*;
public class Mastercard {
public static void main(String[] args) {
JLabel lblCardHolder = new JLabel("Card holder");
JTextField tfCardHolder = new JTextField();
JLabel lblCardNumber = new JLabel("Card number");
JTextField tfCardNumber = new JTextField();
JLabel lblExpirationDate = new JLabel("Expiration date");
JTextField tfExpirationDate = new JTextField();
JComboBox combo = new JComboBox(new String[]{"Visa"});
JPanel panel = new JPanel();
GroupLayout layout = new GroupLayout(panel);
panel.setLayout(layout);
layout.setAutoCreateContainerGaps(true);
layout.setAutoCreateGaps(true);
layout.setHorizontalGroup(layout.createSequentialGroup()
.addGroup(layout.createParallelGroup()
.addComponent(lblCardHolder)
.addComponent(lblCardNumber)
.addComponent(lblExpirationDate))
.addGroup(layout.createParallelGroup()
.addComponent(tfCardHolder)
.addComponent(tfCardNumber)
.addGroup(layout.createSequentialGroup()
.addComponent(tfExpirationDate)
.addComponent(combo, DEFAULT_SIZE, 100, PREFERRED_SIZE))));
layout.setVerticalGroup(layout.createSequentialGroup()
.addGroup(layout.createParallelGroup(BASELINE)
.addComponent(lblCardHolder)
.addComponent(tfCardHolder))
.addGroup(layout.createParallelGroup(BASELINE)
.addComponent(lblCardNumber)
.addComponent(tfCardNumber))
.addGroup(layout.createParallelGroup(BASELINE)
.addComponent(lblExpirationDate)
.addComponent(tfExpirationDate)
.addComponent(combo)));
JFrame f = new JFrame();
f.setContentPane(panel);
f.pack();
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
f.setVisible(true);
}
}
This is the result:
GroupLayout is a powerful constraint-based layout manager. When writing a group layout, you basically just describe whether components (or group of components) should be placed in sequence or in parallel. This description is done independently for the horizontal and vertical axis.
Horizontally, there are 2 groups in sequence : the labels, and the rest. The rest is composed of 3 parallel things : 2 textfields, and a sequence of tfExpirationDate and combo. This is, in plain english, what the layout.setHorizontalGroup(... describes.
Vertically, there are 3 groups in sequence : one composed of a label and a textfield in parallel, another composed of a label and a textfield in parallel, and yet another composed of a label, a textfield and a combobox in parallel. This is what layout.setVerticalGroup(... describes.
The nesting of these groups and components is important, this is why I indented everything correctly.
I find this approach of describing constraints quite easy to reason about, using only 2 concepts : sequential groups and parallel groups. Compared to grid-based layouts, it has the advantage that you don't break everything each time you want to add or move a component.

Ok I figured it out using a complex combination of GridBagLayout and BorderLayout
what a mess.... :)

Related

Aligning Vertical and Horizontal SequentialGroup in Swing

I have written this code
package test;
import java.awt.BorderLayout;
import java.awt.Dimension;
import java.awt.Font;
import javax.swing.*;
import javax.swing.GroupLayout.Alignment;
public class MainFrame extends JFrame
{
private int levels;
private int slots;
private JLabel labelShowLevel;
private JFormattedTextField textShowLevel;
private JButton buttonShowLevel ;
private JLabel labelAddEntity ;
private JFormattedTextField textAddEntity ;
private JButton buttonAddEntity ;
private JComboBox cb;
private JLabel labelRemoveEntity ;
private JFormattedTextField textRemoveEntity ;
private JButton buttonRemoveEntity ;
private JLabel labelSearchEntity ;
private JFormattedTextField textSearchEntity ;
private JButton buttonSearchEntity ;
private JLabel labelEmptySlots ;
private JButton buttonEmptySlots ;
private JLabel levelDispaly;
private JLabel totalLevels;
public MainFrame(int levels, int slots)
{
this.levels = levels;
this.slots = slots;
getContentPane().add( CreatPanel(), BorderLayout.EAST);
this.setDefaultCloseOperation(EXIT_ON_CLOSE);
this.setPreferredSize(new Dimension(1000,500));
this.revalidate();
this.repaint();
this.pack();
this.setVisible(true);
}
JPanel CreatPanel()
{
JPanel panel = new JPanel();
labelShowLevel = new JLabel("Display Level");
labelAddEntity = new JLabel("Enter new car/motorbike");
labelRemoveEntity = new JLabel("Exit car/motorbike");
labelSearchEntity = new JLabel("Find car/motorbike");
labelEmptySlots = new JLabel("Get total empty slots");
textShowLevel = new JFormattedTextField();
textAddEntity = new JFormattedTextField();
textRemoveEntity = new JFormattedTextField();
textSearchEntity = new JFormattedTextField();
textShowLevel.setPreferredSize(new Dimension(100, HEIGHT));
buttonShowLevel = new JButton("Show");
buttonAddEntity = new JButton("Enter");
buttonRemoveEntity= new JButton("Exit");
buttonSearchEntity = new JButton("Search");
buttonEmptySlots = new JButton("Find");
Font font = new Font("sans comic", Font.ITALIC, 18);
levelDispaly = new JLabel("Now Displaying Level 0");
levelDispaly.setFont(font);
totalLevels = new JLabel("Total Levels:"+ this.levels+" Total slots per level:"+this.slots);
String[] items = { "Car", "Motorbike" };
cb = new JComboBox(items);
cb.setSelectedItem(items[0]);
GroupLayout layout = new GroupLayout(panel);
panel.setLayout(layout);
layout.setAutoCreateGaps(true);
layout.setAutoCreateContainerGaps(true);
GroupLayout.SequentialGroup hGroup = layout.createSequentialGroup();
hGroup.addGroup(layout.createParallelGroup().
addComponent(labelShowLevel).addComponent(labelAddEntity).addComponent(labelRemoveEntity).addComponent(labelSearchEntity).addComponent(labelEmptySlots).addComponent(levelDispaly).addComponent(totalLevels));
hGroup.addGroup(layout.createParallelGroup().
addComponent(textShowLevel).addComponent(textAddEntity).addComponent(cb).addComponent(textRemoveEntity).addComponent(textSearchEntity));
hGroup.addGroup(layout.createParallelGroup().
addComponent(buttonShowLevel).addComponent(buttonAddEntity).addComponent(buttonRemoveEntity).addComponent(buttonSearchEntity).addComponent(buttonEmptySlots));
layout.setHorizontalGroup(hGroup);
layout.linkSize(SwingConstants.HORIZONTAL, buttonShowLevel, buttonAddEntity,buttonRemoveEntity,buttonSearchEntity,buttonEmptySlots);
// Create a sequential group for the vertical axis.
GroupLayout.SequentialGroup vGroup = layout.createSequentialGroup();
vGroup.addGroup(layout.createParallelGroup(Alignment.BASELINE).
addComponent(labelShowLevel).addComponent(textShowLevel).addComponent(buttonShowLevel));
vGroup.addGroup(layout.createParallelGroup(Alignment.BASELINE).
addComponent(labelAddEntity).addComponent(textAddEntity));
vGroup.addGroup(layout.createParallelGroup(Alignment.BASELINE).
addComponent(cb).addComponent(buttonAddEntity));
vGroup.addGroup(layout.createParallelGroup(Alignment.BASELINE).
addComponent(labelRemoveEntity).addComponent(textRemoveEntity).addComponent(buttonRemoveEntity));
vGroup.addGroup(layout.createParallelGroup(Alignment.BASELINE).
addComponent(labelSearchEntity).addComponent(textSearchEntity).addComponent(buttonSearchEntity));
vGroup.addGroup(layout.createParallelGroup(Alignment.BASELINE).
addComponent(labelEmptySlots).addComponent(buttonEmptySlots));
vGroup.addGroup(layout.createParallelGroup(Alignment.BASELINE).
addComponent(levelDispaly));
vGroup.addGroup(layout.createParallelGroup(Alignment.BASELINE).
addComponent(totalLevels));
layout.setVerticalGroup(vGroup);
return panel;
}
public static void main(String args[])
{
java.awt.EventQueue.invokeLater(new Runnable() {
public void run()
{
try
{
UIManager.setLookAndFeel("javax.swing.plaf.metal.MetalLookAndFeel");
}
catch (Exception ex) {
ex.printStackTrace();
}
(new MainFrame(5,5)).setVisible(true);
}
});
}
}
and the result is
What I want is to center the last two labels (totalLevels and levelDispaly) and make them take the three columns. I did many trials and faild.
Note that the left side of the pane is empty because I have deleted the unnecessary parts of the code just to concentrate on the problem.
You were close - you fell on a caveat called still developing intuition for GroupLayout. Some people never leave this limbo :)
Jokes aside, here is your MCVE with a few changes:
public class MainFrame extends JFrame {
private int levels;
private int slots;
private JLabel labelShowLevel;
private JFormattedTextField textShowLevel;
private JButton buttonShowLevel;
private JLabel labelAddEntity;
private JFormattedTextField textAddEntity;
private JButton buttonAddEntity;
private JComboBox cb;
private JLabel labelRemoveEntity;
private JFormattedTextField textRemoveEntity;
private JButton buttonRemoveEntity;
private JLabel labelSearchEntity;
private JFormattedTextField textSearchEntity;
private JButton buttonSearchEntity;
private JLabel labelEmptySlots;
private JButton buttonEmptySlots;
private JLabel levelDispaly;
private JLabel totalLevels;
public MainFrame(int levels, int slots) {
this.levels = levels;
this.slots = slots;
getContentPane().add(CreatPanel());
this.setDefaultCloseOperation(EXIT_ON_CLOSE);
this.pack();
this.setVisible(true);
}
JPanel CreatPanel() {
JPanel panel = new JPanel();
labelShowLevel = new JLabel("Display Level");
labelAddEntity = new JLabel("Enter new car/motorbike");
labelRemoveEntity = new JLabel("Exit car/motorbike");
labelSearchEntity = new JLabel("Find car/motorbike");
labelEmptySlots = new JLabel("Get total empty slots");
textShowLevel = new JFormattedTextField();
textAddEntity = new JFormattedTextField();
textRemoveEntity = new JFormattedTextField();
textSearchEntity = new JFormattedTextField();
textShowLevel.setPreferredSize(new Dimension(100, HEIGHT));
buttonShowLevel = new JButton("Show");
buttonAddEntity = new JButton("Enter");
buttonRemoveEntity = new JButton("Exit");
buttonSearchEntity = new JButton("Search");
buttonEmptySlots = new JButton("Find");
Font font = new Font("sans comic", Font.ITALIC, 18);
levelDispaly = new JLabel("Now Displaying Level 0");
levelDispaly.setFont(font);
totalLevels = new JLabel("Total Levels:"+ this.levels + " Total slots per level:"
+ this.slots);
String[] items = {"Car", "Motorbike"};
cb = new JComboBox(items);
cb.setSelectedItem(items[0]);
GroupLayout layout = new GroupLayout(panel);
panel.setLayout(layout);
layout.setAutoCreateGaps(true);
layout.setAutoCreateContainerGaps(true);
layout.linkSize(SwingConstants.HORIZONTAL, buttonShowLevel, buttonAddEntity, buttonRemoveEntity, buttonSearchEntity, buttonEmptySlots);
//#formatter:off
// Horizontal
GroupLayout.ParallelGroup hGroup = layout.createParallelGroup(Alignment.CENTER); // Will align the labels the way you wanted
hGroup.addGroup(layout.createSequentialGroup()
.addGroup(layout.createParallelGroup()
.addComponent(labelShowLevel)
.addComponent(labelAddEntity)
.addComponent(labelRemoveEntity)
.addComponent(labelSearchEntity)
.addComponent(labelEmptySlots))
.addGroup(layout.createParallelGroup()
.addComponent(textShowLevel)
.addComponent(textAddEntity)
.addComponent(cb)
.addComponent(textRemoveEntity)
.addComponent(textSearchEntity))
.addGroup(layout.createParallelGroup()
.addComponent(buttonShowLevel)
.addComponent(buttonAddEntity)
.addComponent(buttonRemoveEntity)
.addComponent(buttonSearchEntity)
.addComponent(buttonEmptySlots)));
hGroup.addComponent(levelDispaly);
hGroup.addComponent(totalLevels);
layout.setHorizontalGroup(hGroup);
// Vertical
GroupLayout.SequentialGroup vGroup = layout.createSequentialGroup();
vGroup.addGroup(layout.createParallelGroup()
.addComponent(labelShowLevel)
.addComponent(textShowLevel)
.addComponent(buttonShowLevel));
vGroup.addGroup(layout.createParallelGroup()
.addComponent(labelAddEntity)
.addComponent(textAddEntity));
vGroup.addGroup(layout.createParallelGroup()
.addComponent(cb)
.addComponent(buttonAddEntity));
vGroup.addGroup(layout.createParallelGroup()
.addComponent(labelRemoveEntity)
.addComponent(textRemoveEntity)
.addComponent(buttonRemoveEntity));
vGroup.addGroup(layout.createParallelGroup()
.addComponent(labelSearchEntity)
.addComponent(textSearchEntity)
.addComponent(buttonSearchEntity));
vGroup.addGroup(layout.createParallelGroup()
.addComponent(labelEmptySlots)
.addComponent(buttonEmptySlots));
vGroup.addGroup(layout.createParallelGroup()
.addComponent(levelDispaly));
vGroup.addGroup(layout.createParallelGroup()
.addComponent(totalLevels));
layout.setVerticalGroup(vGroup);
//#formatter:on
return panel;
}
public static void main(String args[]) {
EventQueue.invokeLater(() -> new MainFrame(5, 5));
}
}
Your problem was in the horizontal group. You restricted your labels to be aligned with the left column. As a general rule of thumb, if you want components which are unrestricted (free to be aligned) in a certain direction, then you need to add another "level" of groups in that direction's hierarchy.
Specifically, after removing the 2 labels from your horizontal code, you are left with a sequential group (hGroup in your case) which has parallel groups in it. Since the 2 labels are not to be aligned with these groups, they must be added to a sequential group that is not hGroup. That means that if hGroup is sequential, it will have a single "child" parallel group, which will have under it the new sequential group (that replaces hGroup) along with the 2 labels:
hGroup (seq.)
p (par.)
s (Seq.)
p1
p2
p3
label1
label2
(not showing the components under p1-p3.)
However, since hGroup now has only 1 child, you can just remove this redundant top-level sequential group and make hGroup that single parallel group. Remember that both your horizontal and vertical groups can be either one of sequential or parallel groups. Don't restrict yourself to having both directions sequential since that just adds redundant groups (it will still work):
hGroup (par.)
s (Seq.)
p1
p2
p3
label1
label2
The following are general thinking patterns that can help you with GroupLayout. Do note that there are many ways one can think about these and come up with alternative explanations. After a while with Grouplayout this will become completely intuitive.
Choosing the right type of parent group
If you want to know if your vertical/horizontal group should be sequential or parallel, just look at the "governing direction". The "governing direction" is the direction in which you can definitively tell the sequence of the components at the highest level.
In your case, let's look at vertical group. When looking from top to bottom, you can tell definitively the order of components (or groups thereof) from top to bottom, but not from left to right because the last 2 labels don't have a definitive left to right order with respect to the other components - these 2 labels are determined by alignment. Then
Vertical group && Vertical governing direction == Sequential group
For the horizontal group, again you can only tell the top to bottom order:
Horizontal group && Vertical governing direction == Parallel group
If there is no single "governing direction", you can choose either. Imagine a 2x2 grid, there are 4 layout hierarchies which will get the same result.
If you make a mistake and end up with the wrong type of group, you will have a group with only 1 child group of the other type. Then you can just cut that top-level group.
Creating the group hierarchy
After choosing the "governing direction", start advancing in that direction and adding the components in that definitive order. If there is more than 1 component with the same place in the order, it means that you need to add a group in the other direction.
Observe the following schematics for the vertical (red arrow) sequential group:
Starting from top to bottom, there are 3 components in the 1st location of the order (red rectangle). That means that we need a parallel group to section (separate) each component in a sub-hierarchy (green lines). This process continues until we reach the first label, at which point there are no "competing" components, so we just add that component (red rectangle). Same for the second label. This is 1-to-1 the code for the vertical group.
Observe a similar schematics for the horizontal (blue arrow) parallel group:
Starting from top to bottom, there are 3 components in the first location. This means that we need to add a sequential group. However, each of these 3 component shares its sequential location with other components horizontal to it. This means that we need to add 3 parallel groups (blue rectangles) in order to section the components. These 3 parallel groups are added to the sequential group (green rectangle). Then we reach the first label and just add that component (green rectangle). Same for the second label. This is 1-to-1 the code for the horizontal group.
Alignment
I suggest not worrying about alignment at all until you got the group hierarchies the way you wanted. In the cases (such as yours) where you link components, alignment doesn't play a role anyway since the components already fill the whole space (check for yourself). Setting alignments is quick and easy after everything is set.
Non-layout-related notes:
No need to call revalidate and repaint before displaying the frame.
The call setSize(...) is redundant since you later call pack (as you should).
You are calling *setVisible(true)twice: once inmain` and one in the constructor. One of them does simply nothing, just pick one.
Don't call setPrefferedSize(...) on textShowLevel. I'm not even sure what you are passing to it (some ImageObserver thing). You can set its width by specifying the number of columns it contains or by overriding its getPreferredSize().
Don't use the raw type JComboBox, you probably want <String>.
I didn't add the panel to BorderLayout.EAST because it made no sense in my context.

Java Styling - Spacing

So I've been learning Java for the very first time and it's time for me to attempt my first project. And I'm stuck at the "first hurdle" haha.
The issue I have is the fact that I don't actually know how to space J Items apart.
I have a 250,350 window for a Log In form with a JLabel, a JTextField for username and JLabel JPassword for Password with a JButton at the bottom.
What I want to do now is style it so that the spacing between the top and the bottom of the form makes it so that the form is centered as well as adding a line's height space between the JLabel and the JTextField. (Basically a \n type deal but that isn't working.)
Hopefully this makes sense, if not, I apologise and I'll try to rephrase/add code!
public Game() {
this.setSize(250,350);
this.setLocationRelativeTo(null);
this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
this.setTitle("Sticket Cricket - Login");
JPanel loginMenuPanel = new JPanel();
loginButton = new JButton("Login");
usernameField = new JTextField();
usernameField.setColumns(10);
passwordField = new JPasswordField();
passwordField.setColumns(10);
passwordField.requestFocus();
usernameLabel = new JLabel("Username: ");
passwordLabel = new JLabel("Password: ");
this.add(loginMenuPanel);
loginMenuPanel.add(usernameLabel);
loginMenuPanel.add(usernameField);
loginMenuPanel.add(passwordLabel);
loginMenuPanel.add(passwordField);
loginMenuPanel.add(loginButton);
this.setVisible(true);
}
Short Answer:
Create a JPanel, set the layoutmanger of the panel (some examples, GridLayout, BorderLayout, Check out the tutorial here where more of these are explained)
Then add your components to this panel accordingly
For the layout you are looking for it would possibly be easier to use an IDE to create this, I find Net Beans to be the easiest for doing this.
My recommendation would be for you to create a JPanel with a grid layout of 2 columns and 2 rows, to this add you JLabels and Text fields for the logon name and password.
Then create another JPanel possibly BorderLayout or Flow Layout and add the above panel to this then add this parent panel to the frame.

How to improve GUI by changing BoxLayout into a different LayoutManager?

I've a big problem with Swing in Java, I used BoxLayout for this but still it looks bad.
Any suggestions about my usage of layouts, or how to change it to look like in assumptions? (here are assumptions)
Container main = new Container();
Container left = new Container();// here goin buttons
Container right = new Container(); // here goin tabs + more buttons, textfields and other stuff
BoxLayout lewyL = new BoxLayout(left, BoxLayout.Y_AXIS);
left.setLayout(lewyL);
left.add(rastrowa); //radiobutton
left.add(wektorowa);//radiobutton
left.add(apDwuliniowa);//checkbox
left.add(wczytaj);//button
left.add(zapisz);//obutton
left.add(wyczysc);//button
BoxLayout prawyL = new BoxLayout(right, BoxLayout.Y_AXIS);
right.setLayout(prawyL);
right.add(zakladki);// tabs (mostly i use BoxLayout but for last one i need something more "complicated")
EDIT: I almost solve this problem, I need to move all elements to left (how it look like)but I have no idea how ;/ Here is constructor of this class.
JLabel label = new JLabel("O wektor");
JLabel labelA = new JLabel("a:");
JLabel labelB = new JLabel("b:");
JButton wykonaj = new JButton("Wykonaj");
JTextField a = new JTextField(5);
JTextField b = new JTextField(5);
add(label);
add(labelA);
add(a);
add(labelB);
add(b);
add(wykonaj);
There's nothing wrong with the way it looks (in my opinion), but if you want it to look a little better, why don't you convert the left panel (which is 6x1) into a 3x2 panel, with the checkboxes/radiobuttons on the left, and buttons on the right? Sounds like a job for GridLayout - one of my favorite classes...
JPanel leftPanel = new JPanel(new GridLayout(3,2));
leftPanel.add(rastrowa); //radiobutton
leftPanel.add(wczytaj); //button
leftPanel.add(wektorowa); //radiobutton
leftPanel.add(zapisz); //obutton
leftPanel.add(apDwuliniowa); //checkbox
leftPanel.add(wyczysc); //button
Note that the 3,2 defines the number of rows,columns. When adding panels, they are added to the grid from left-to-right, and top-to-bottom. GridLayout also auto-sizes the components, so all the buttons etc will be the same width and height, making it look more consistent.
The GridLayout documentation might be useful, and the Visual Guide to Layout Managers is a great place to see other layout managers that might work better for your different situations. I personally find BorderLayout and GridLayout to be the most useful, and cover about 95% of the situations I ever need for my GUIs.

Please help me understanding BoxLayout alignment issues

I'm trying to create a very simple window using Java Layouts. I have got three elements to arrange: a button, a progress bar and a label. The button has to be vertically centered, the progress bar has to take full width, and the label has to be left aligned.
Here's some code (just assume pane is the content pane of a JFrame, and button, progressBar and label have been created before):
BoxLayout layout = new BoxLayout(pane, BoxLayout.Y_AXIS);
pane.setLayout(layout);
button.setAlignmentX(Component.CENTER_ALIGNMENT);
pane.add(button);
progressBar.setAlignmentX(Component.CENTER_ALIGNMENT);
pane.add(progressBar);
label.setAlignmentX(Component.LEFT_ALIGNMENT);
pane.add(label);
When I test the application I see everything misaligned and screwed up: the button and the label are randomly indented, and if I resize the window the indentation amount changes in a strange way.
The progress bar looks good (full width).
I just don't understand what's happening. Can you give me a clue?
BoxLayout cannot handle different alignments: see http://download.oracle.com/javase/tutorial/uiswing/layout/box.html
quoting from that article: "In general, all the components controlled by a top-to-bottom BoxLayout object should have the same X alignment. Similarly, all the components controlled by a left-to-right Boxlayout should generally have the same Y alignment."
Sometimes you need to get a little creative and use nested panels. But I like this approach better then trying to learn and memorize all the constraints required when using other layout managers (GridBagLayout, GroupLayout) there where designed to be used by IDE's that generate code for you.
import java.awt.*;
import javax.swing.*;
public class BoxLayoutVertical extends JFrame
{
public BoxLayoutVertical()
{
Box box = Box.createVerticalBox();
JButton button = new JButton("A button");
button.setAlignmentX(Component.CENTER_ALIGNMENT);
box.add(button);
JProgressBar progressBar = new JProgressBar(0, 100);
progressBar.setAlignmentX(Component.CENTER_ALIGNMENT);
box.add(progressBar);
JPanel panel = new JPanel( new BorderLayout() );
JLabel label = new JLabel("A label");
label.setAlignmentX(Component.LEFT_ALIGNMENT);
panel.add(label);
box.add(panel);
add(box, BorderLayout.NORTH);
}
public static void main(String[] args)
{
BoxLayoutVertical frame = new BoxLayoutVertical();
frame.setDefaultCloseOperation( EXIT_ON_CLOSE );
frame.setSize(300, 200);
frame.setLocationRelativeTo( null );
frame.setVisible(true);
}
}
To complement my comment to the original question, here is a snippet that uses DesignGridLayout:
JButton button = new JButton("Button");
JProgressBar progressBar = new JProgressBar();
JLabel label = new JLabel("Label");
// The interesting stuff is in the next 4 lines
DesignGridLayout layout = new DesignGridLayout(getContentPane());
layout.row().center().add(button).withOwnRowWidth();
layout.row().center().fill().add(progressBar);
layout.row().left().add(label);
pack();
It does exactly what wou describe in your question and doesn't require any specific call of any of the components.
Maybe your code is just a snippet, but I'm missing a call to pack().
Coding swing layout by hand can be very frustrating with the standard Layout managers. I use MiG Layout for that purpose. It is straight forward and you have a nice layout with just a few lines of code. If you're not forced to use BoxLayout I would suggest you give it a try.
Don't use BoxLayout. It works only for very simple cases.
For your case, I would recommend either GridBagLayout or (my favorite) GroupLayout.
For GroupLayout, I created a subclass (LayoutHelper) with some utility methods and useful constructors, which makes writing the Layout much easier.
Of course, usually I align all components in a group the same way, so it is not as short in your case as it would be in the simple case.
LayoutHelper h = new LayoutHelper(pane);
h.setVerticalGroup
( h.sequential( button, progressBar, label));
h.setHorizontalGroup
( ((ParallelGroup)h.parallel())
.addComponent(button, Alignment.CENTER)
.addComponent(progressBar)
.addComponent(label, Alignment.TRAILING));
Here is a screenshot:
For a simple "everything aligned the same way", the horizontal group would look like this:
h.setHorizontalGroup
( h.parallel (button, progressBar, label));
(optionally with a first argument indicating the alignment).

Stretching in Java Swing Box

I have a code:
Box box_general = Box.createHorizontalBox();
Box box_panel1 = Box.createVerticalBox();
Box box_panel2 = Box.createVerticalBox();
JPanel jpanel_1 = new JPanel();
jpanel_1.setPreferredSize(new Dimension(50, 152));
jpanel_1.setOpaque(true);
jpanel_1.setBorder(BorderFactory.createLineBorder(Color.BLUE));
JPanel jpanel_2 = new JPanel();
jpanel_2.setPreferredSize(new Dimension(340, 152));
jpanel_2.setOpaque(true);
jpanel_2.setBorder(BorderFactory.createLineBorder(Color.RED));
JTextField jtxtf_populationSize = new JTextField();
jtxtf_populationSize.setSize(10, 20);
JTextField jtxtf_processorsAmount = new JTextField();
JButton jbtn_loadProcesses = new JButton("File path");
box_panel1.add(Box.createRigidArea(new Dimension(0,4)));
box_panel1.add(jtxtf_processorsAmount);
box_panel1.add(Box.createRigidArea(new Dimension(0,20)));
box_panel1.add(jbtn_loadProcesses);
jpanel_1.add(box_panel1);
JLabel jlbl_populationSize = new JLabel("Enter the population size");
JLabel jlbl_processorsAmount = new JLabel("Enter the amount of processors");
JLabel jlbl_loadProcesses = new JLabel("Load processes from file");
jlbl_populationSize.setFont(font);
jlbl_processorsAmount.setFont(font);
jlbl_loadProcesses.setFont(font);
box_panel2.add(jlbl_populationSize);
box_panel2.add(Box.createRigidArea(new Dimension(0,4)));
box_panel2.add(jlbl_processorsAmount);
box_panel2.add(Box.createRigidArea(new Dimension(0,15)));
box_panel2.add(jlbl_loadProcesses);
jpanel_2.add(box_panel2);
box_general.add(jpanel_2);
box_general.add(Box.createRigidArea(new Dimension(10,0)));
box_general.add(jpanel_1);
It creates 3 boxes, where the general box contains two other boxes. The problem is: in the box all the compopents are stretched of width. For example, there is a line jtxtf_populationSize.setSize(10, 20); but this text field is stretched in the box_panel1 on width. I tried to use a BoxLayout with it's Alignment_X but this didn't work.
Could you, please, advise me, what to do - how to avoid stretching?
Most of the swing layouts will use the preferred size and the minimum/maximum size over a call to setSize. The key here is to get the right preferred and minimums so that they don't shrink too much, and then insert a strut (Box#createHorizontalStrut) to fill up space where you don't want a component.
With complex layouts like this, consider the SpringLayout, which admittedly has a higher learning curve, but once you get used to it, will allow you to more naturally state the constraints you want.

Categories