Java : gridbaglayout confusion - java

this is my first question on here, so please forgive me when i am breaking any rules, or not using the right format
I am creating a simple form in java swing which consists of 1 JLabel, 1 JTextField, and 1 Button
|---------------------------|
| |
| JLabel |
| |
|---------------------------|
| JTextField | Button |
|---------------------------|
The Button should be in the bottom-right corner, the JTextField left of it, the JLabel on the top, spanning both columns
I want the Button to be a fixed size, the JTextField a fixed height, but using the full width (except for what is in use by the Button), and the JLabel using all other space (with and height)
I am not even sure if i should use a GridBagLayout or another Layout ?
This is probably a very easy question, but got me puzzled for quite some time (too many options with the GridBarLayout i guess)

First, set the layout of your panel to GridBagLayout.
Then, create a GridBagConstraints object and set the fill to GridBagConstraints.BOTH.
For the JLabel, set the following properties on the constraints object: gridx = 0, gridy = 0, gridwidth = 2, gridheight = 2, weightx = 1, weighty = 1.
For the JTextField, set the following properties on the constraints object: gridx = 0, gridy = 1, gridwidth = 1, gridheight = 1, weightx = 1, weighty = 0.
For the JButton, set the following properties on the constraints object: gridx = 1, gridy = 1, gridwidth = 1, gridheight = 1, weightx = 0, weighty = 0.

Class BorderLayout is easy to use, less powerful than GridBagLayout.
But when thing are simple, solution have to be the same.
panel.add( label, BorderLayout.CENTER );
JPanel south = new JPanel();
south.add( textfield );
south.add( button );
button.setPreferredSize( x, y );
panel.add( south, BorderLayout.SOUTH );

OK here is a demo code that should get you going:
import java.awt.Color;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import javax.swing.BorderFactory;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JTextField;
import javax.swing.SwingUtilities;
public class TestGridBagLayout {
protected void initUI() {
JFrame frame = new JFrame(TestGridBagLayout.class.getSimpleName());
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
JButton button = new JButton("A button");
JTextField textField = new JTextField();
JLabel label = new JLabel("A cool long nice label that will stretch.");
label.setHorizontalAlignment(JLabel.CENTER);
label.setBorder(BorderFactory.createLineBorder(Color.GREEN));
JPanel panel = new JPanel(new GridBagLayout());
GridBagConstraints gbc = new GridBagConstraints();
gbc.fill = GridBagConstraints.BOTH;// Fill the "cell" in both direction
gbc.weightx = 1.0;// Allocate extra-width to the label
gbc.weighty = 1.0;// Allocate extra-height to the label
gbc.gridwidth = GridBagConstraints.REMAINDER;// The label takes all the available width of the "row"
panel.add(label, gbc);
gbc.weighty = 0; // Don't stretch TF vertically
gbc.fill = GridBagConstraints.HORIZONTAL; // Fill horizontally
gbc.gridwidth = GridBagConstraints.RELATIVE;
panel.add(textField, gbc);
gbc.weightx = 0; // No extra horizontal space is given to the button
gbc.fill = GridBagConstraints.NONE; // No fill for the button
panel.add(button, gbc);
frame.add(panel);
frame.pack();
frame.setVisible(true);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
new TestGridBagLayout().initUI();
}
});
}
}

I don't know this is a common thing to do, but below is code which is working (mainly code from Dan and Guillaume)
//show stuff
setLayout(new GridBagLayout());
GridBagConstraints c = new GridBagConstraints();
//show label
c.fill = GridBagConstraints.BOTH; // Fill the "cell" in both direction
c.weightx = 1.0; // Allocate extra-width to the label
c.weighty = 1.0; // Allocate extra-height to the label
c.gridwidth = GridBagConstraints.REMAINDER; // The label takes all the available width of the "row"
add(mlblShow,c);
//show cmd txt
c.weighty = 0; // Don't stretch TF vertically
c.fill = GridBagConstraints.BOTH; // Fill horizontally and vertically
c.gridwidth = GridBagConstraints.RELATIVE;
add(mtxtCmd,c);
//show send button
c.weightx = 0; // No extra horizontal space is given to the button
c.fill = GridBagConstraints.NONE; // No fill for the button
add(cmdSend,c);

Related

Java component positioning on GridBagLayout

I am trying to position components on JPanel using GridBagLayout but the output I am getting is completely off from what I expect. Hope to get some clarity with brilliant-minded ppl in stackoverflow:).
Below I have provided a piece of code and screentshot of the program. My questions are:
Why the JLabel Choose measure system to convert is not on Y-axis = 1? As to my knowledge, c.gridy=1 is one pixel downward, but the label is stuck on the top leaving no space from the Frame title. And also, why is it positioned so weird, i.e., not really in the center, nor in the start?
Why is there such a big space between ComboBoxes From... and To..., but there is no space between ComboBox To... and TextField Enter value here...?
Here is the code:
JPanel container = new JPanel();
container.setLayout(new GridBagLayout());
getContentPane().add(container, BorderLayout.NORTH);
TitledBorder outputCenter;
GridBagConstraints c = new GridBagConstraints();
label = new JLabel("Choose measure system to convert");
label.setFont(new Font("Times New Roman", Font.PLAIN, 20));
c.gridx = 0;
c.gridy = 1;
container.add(label, c);
fromList = new JComboBox<String>(convertFrom);
c.gridx = 0;
c.gridy = 2;
container.add(fromList, c);
toList = new JComboBox<String>(convertTo);
c.gridx = 1;
c.gridy = 2;
container.add(toList, c);
//Field where user enters the value to be converted
input = new JTextField("Enter value here...");
input.setPreferredSize(new Dimension(150,30));;
input.setEditable(true);
input.setBackground(Color.WHITE);
input.setBorder(BorderFactory.createLineBorder(Color.BLACK));
input.addMouseListener(new MouseAdapter(){
public void mouseClicked(MouseEvent e){
input.setText("");}});
c.gridx = 2;
c.gridy = 2;
container.add(input, c);
And here is the screentshot:
EDIT: If I change the code from:
label = new JLabel("Choose measure system to convert");
label.setFont(new Font("Times New Roman", Font.PLAIN, 20));
c.gridx = 0;
c.gridy = 1;
container.add(label, c);
label = new JLabel("Choose measure system to convert");
label.setFont(new Font("Times New Roman", Font.PLAIN, 20));
c.gridx = 1; // changed this line
c.gridy = 1;
container.add(label, c);
The outcome is like this:
This is very confusing me as why changing the position of one component effects everything?
GridBagConstraints sets up the frame into effectively a grid. The width and height of the cells in the grid are determined by the size of the data in the cell by default unless otherwise specified. So if you want to add some space in-between cells I suggest ipadx and ipady. You can also utilize anchor for adjusting your data in the cell. I also suggest weightx and weighty for adjusting the actual cell size.
So imagine something like this as your current set up:
EDIT: Example of what your new GBC looks like. The numbers are (gridx,gridy)
Why the JLabel Choose measure system to convert is not on Y-axis = 1? As to my knowledge, c.gridy=1 is one pixel downward
You're confusing yourself, c.gridy = 1 is not positioning it 1 pixel downward but rather on the next row, but as there's no previous row, then it takes the first row. For reference see: GridBagConstraints#gridy which says the following:
Specifies the cell at the top of the component's display area, where the topmost cell has gridy=0. The value RELATIVE specifies that the component be placed just below the component that was added to the container just before this component was added.
Next question:
And also, why is it positioned so weird, i.e., not really in the center, nor in the start?
It is centered in its own cell, if you want to center it on the JFrame, then you might need to create it on its own gridx = 1 and the rest of components on the other ones (0 and 2) or make it span 2 or more columns based on you want it to look like...
Why is there such a big space between ComboBoxes From... and To..., but there is no space between ComboBox To... and TextField Enter value here...?
It is because your program is giving it all the extra space because of the large text on the first cell...
You can have something like this:
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.Insets;
import javax.swing.JComboBox;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JTextField;
import javax.swing.SwingUtilities;
public class GridBagLayoutExample {
private JFrame frame;
private JPanel pane;
private JLabel label;
private JTextField field;
private JComboBox<String> box1;
private JComboBox<String> box2;
private GridBagConstraints gbc;
public static void main(String[] args) {
SwingUtilities.invokeLater(new GridBagLayoutExample()::createAndShowGui);
}
private void createAndShowGui() {
frame = new JFrame(getClass().getSimpleName());
pane = new JPanel();
pane.setLayout(new GridBagLayout());
gbc = new GridBagConstraints();
label = new JLabel("Choose measure system to convert");
box1 = new JComboBox<>(new String[] {"From..."});
box2 = new JComboBox<>(new String[] {"To..."});
field = new JTextField(10);
gbc.insets = new Insets(5, 5, 5, 5); //We add extra space at top, left, bottom, right of each component
gbc.gridx = 0;
gbc.gridy = 0;
gbc.gridwidth = 3; //We make our text to span 3 cells
pane.add(label, gbc);
gbc.gridy = 1;
gbc.gridwidth = 1; //We return the spanning to 1 single cell
pane.add(box1, gbc);
gbc.gridx = 1;
pane.add(box2, gbc);
gbc.gridx = 2;
pane.add(field, gbc);
frame.add(pane);
frame.pack();
frame.setVisible(true);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
}
}
Which produces the following output:

GridBagLayout not lining up

I have 5 JPanels lined up vertically. Each JPanel is filled with the same elements, but different values (JPanel, JButton, JLabel). I want them to look like this:
Panel Button Label
Panel Button Label
Buts it's turning out like this
Panel Button Label
Panel Button Label
The spacing is a little off, but the code is exactly the same for each container. How can I fix this?
public class AnswerChoice extends JPanel {
private static final long serialVersionUID = 1L;
private AnswerButton button;
private JLabel answerLabel;
public AnswerChoice(String imageURL) {
setBackground(Color.RED);
setLayout(new GridBagLayout());
button = new AnswerButton(imageURL);
answerLabel = new JLabel();
answerLabel.setFont(new Font("Times New Roman", Font.PLAIN, 32));
GridBagConstraints gbc = new GridBagConstraints();
JPanel panel = new JPanel();
panel.setBackground(Color.ORANGE);
gbc.fill = GridBagConstraints.BOTH;
gbc.gridx = 1;
gbc.weightx = 0.2;
add(panel, gbc);
gbc.fill = GridBagConstraints.NONE;
gbc.gridx = 2;
gbc.weightx = 0.0;
gbc.insets = new Insets(0, 0, 0, 30);
add(button, gbc);
gbc.fill = GridBagConstraints.BOTH;
gbc.gridx = 3;
gbc.weightx = 0.8;
add(answerLabel, gbc);
}
}
The JLabel does have text in it but I set it somewhere else.
but the code is exactly the same for each container.
The layout is done independently for each container based on the components added to the containter. So the size of each component on the container matters. Each container doesn't know that you have 4 other containers.
I have 5 JPanels lined up vertically.
So then you need to create a single panel using a GridBagLayout and add all 15 components to that panel. Then all 3 columns will be sized based on the components in each of the 5 rows.
Or I see in your logic that you try to assign relative sizes to each of the 3 components as .2, 0, .8. In this case you could use the Relative Layout on each of the panels. Using the Relative Layout you would display the button at its preferred size and then use 0.2f and 0.8f as the contraints for the panel and label respectively.

Setting GridBagLayout grid's size

I'm trying to create a console using Java Swing (GridBagLayout).
I don't know why, but as you can see at the left margin, grids don't have the correct size.
It's supposed to be shown this way:
Where light blue is the list, green the image, orange the text panel and yellow the text field.
I don't know how to make the list bigger and the image smaller. Too, the text field's grid is binded to the list one, even tough the list is on y 1 and the text field on y 2.
Here's some code.
// Command List
DefaultListModel<String> listInput = new DefaultListModel<String>();
JList<String> list = new JList<String>(listInput);
list.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
JScrollPane scrollPane = new JScrollPane(list);
list.setBackground(new Color(160, 160, 160));
list.setSelectionBackground(new Color(150, 150, 150));
scrollPane.setPreferredSize(new Dimension(20, 20));
manager.setCommandList(listInput);
c.insets = new Insets(2, 2, 2, 2);
c.ipady = 0;
c.ipadx = 100;
c.gridx = 0;
c.gridy = 1;
c.gridwidth = 1;
c.gridheight = 2;
c.weightx = 0.1;
c.weighty = 0.6;
c.fill = GridBagConstraints.BOTH;
console.add(scrollPane, c);
// Image Displayer
JLabel image = new JLabel(new ImageIcon());
manager.setImageField(image);
c.insets = new Insets(0, 0, 0, 0);
c.ipady = 0;
c.ipadx = 0;
c.gridx = 0;
c.gridy = 0;
c.gridwidth = 1;
c.gridheight = 1;
c.weightx = 0.1;
c.weighty = 0.3;
c.fill = GridBagConstraints.BOTH;
console.add(image, c);
where 'c' is a grid bag constraint and console the main JPanel.
As you can see, the list has a grid height of 2 and weight of 0.6, and the image a grid height of 1 and weight of 0.9, so not sure why the list is 4 times smaller than the image.
Another issue, I've added a listener to the JLabel holding the image (on resize), anyways, it isn't called. Should I add the listener to the main panel? as the image is only being resized by the layout manager.
Thanks ^^
EDIT:
SSCCE:
package co.relieved.jelly.application.display.swing;
import java.awt.BorderLayout;
import java.awt.Dimension;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.GridLayout;
import java.awt.Insets;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import javax.imageio.ImageIO;
import javax.swing.DefaultListModel;
import javax.swing.ImageIcon;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JList;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTabbedPane;
import javax.swing.JTextField;
import javax.swing.JTextPane;
import javax.swing.ListSelectionModel;
#SuppressWarnings("serial")
public class Test extends JPanel {
static JLabel image;
public static void main(String[] args) {
display();
try {
BufferedImage buffer = ImageIO
.read(new File("/home/juanco/Pictures/Screenshot from 2016-02-08 22-43-22.png"));
image.setIcon(new ImageIcon(
buffer.getScaledInstance(image.getWidth(), image.getHeight(), BufferedImage.SCALE_SMOOTH)));
} catch (IOException ex) {
ex.printStackTrace();
}
}
public Test() {
super(new GridLayout(1, 1));
JTabbedPane tabs = new JTabbedPane();
/*** >>> Console Pane <<< ***/
JPanel console = new JPanel();
console.setLayout(new GridBagLayout());
GridBagConstraints c = new GridBagConstraints();
c.ipadx = 0;
c.ipady = 0;
c.gridwidth = 1;
c.anchor = GridBagConstraints.CENTER;
c.fill = GridBagConstraints.BOTH;
// Console Screen
JTextPane screen = new JTextPane();
screen.setEditable(false);
c.gridx = 1;
c.gridy = 0;
c.gridheight = 2;
c.weightx = 0.8;
c.weighty = 1;
console.add(screen, c);
// Console Input
JTextField input = new JTextField();
c.insets = new Insets(2, 0, 2, 0);
c.ipady = 3;
c.gridy = 2;
c.gridheight = 1;
c.weighty = 0;
c.fill = GridBagConstraints.HORIZONTAL;
console.add(input, c);
// Command List
DefaultListModel<String> listInput = new DefaultListModel<String>();
listInput.setSize(1);
JList<String> list = new JList<String>(listInput);
list.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
JScrollPane scrollPane = new JScrollPane(list);
c.insets = new Insets(2, 2, 2, 2);
c.ipady = 0;
c.ipadx = 100;
c.gridx = 0;
c.gridy = 1;
c.gridheight = 2;
c.weightx = 0.1;
c.weighty = 0.6;
c.fill = GridBagConstraints.BOTH;
console.add(scrollPane, c);
// Image Displayer
image = new JLabel(new ImageIcon());
c.insets = new Insets(0, 0, 0, 0);
c.ipadx = 0;
c.gridy = 0;
c.gridheight = 1;
c.weighty = 0.3;
console.add(image, c);
// General
tabs.addTab("Console", console);
/*** >>> Logs Pane <<< ***/
JPanel logs = new JPanel();
tabs.addTab("Logs", logs);
// Setup
tabs.setSelectedIndex(0);
add(tabs);
}
static void display() {
JFrame frame = new JFrame("Relieved Console");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setPreferredSize(new Dimension(800, 500));
frame.add(new Test(), BorderLayout.CENTER);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
}
Here's some code.
Which doesn't help. The grid can only be completed with the entire code. That is we need to know the gridwidth and gridheight for all components in order to determine the allocation of space to each component in the grid.
the text field's grid is binded to the list one, even tough the list is on y 1 and the text field on y 2.
You can't just randomly assign a component to a grid. The component will only go to grid 2 if the component above it has a grid height of 2. So basically each of your columns needs to have components with a total grid height of 3.
I don't know how to make the list bigger
Setting a preferred size of (20, 20) doesn't help. Anyway you should not be using the setPreferredSize() method.
Instead you should be using:
list.setVisibleRowCount(...);
to specify the visible rows. Then the JList can determine its own preferred size.
Another layout option is to use nested panels which can simplify the layout.
So you could start with a "west" panel that uses a BorderLayout. Then you add the label to "PAGE_START" and the list to "CENTER".
Then you create a "center" panel. Add the main component to the "CENTER" and the text field to the "PAGE_START".
Then you add the two panels to the frame:
frame.add(westPanel, BorderLayout.LINE_START);
frame.add(centerPanel, BorderLayout.CENTER);
Edit:
Sorry, I take back my comment about making each column have a grid height of 3. You can't just specify a total grid height of 3 because you only have 2 components in each column, so each component can only have a height of 1.
Check out my answer in this posting: Why does this GridBagLayout not appear as planned? for a hack that allows you to manipulate gridHeight/Weight by using invisible components in a row/column.
However, I don't recommend that approach. it will be far easier to use my suggestion of nested panels using a BorderLayout (or some other layout manager on the nested panels).
It seems to me that you would need to also specify the gridwidth and/or gridheight for the text field as well.
GridBagLayout is like a glorified GridLayout. It will attempt to align all of the components with the nearest grid spaces around it. Oracle's Tutorials also describe this behavior.
Much of the time, issues with laying out components using GridBagLayout come from the other components being added to the layout instead of the apparent problem child.
I fixed it splitting the "console" JPanel in two, a border layout panel to the left and a grid bag layout panel to the right. As #camickr suggested

GridBagLayout erratic resizing and displaying

I'm making a program that uses a GridBagLayout in a container. My primary use for this is to have two JPanels, which occupy 75% and 25% of the horizontal space of the window. For some reason though, the two panels look more like 90/10, and when resizing, the smaller one rapidly changes in size, between it's apparent minimum size, and what I believe is the desired 25%.
Here is the relevant code.
frmReedreadV = new JFrame();
frmReedreadV.setBounds(x, y, width, height);
frmReedreadV.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frmReedreadV.getContentPane().setLayout(new BoxLayout(frmReedreadV.getContentPane(), BoxLayout.Y_AXIS));
JPanel stretchyPanel = new JPanel();
frmReedreadV.getContentPane().add(stretchyPanel);
stretchyPanel.setLayout(new CardLayout(0, 0));
JPanel textAndUsers = new JPanel(new GridBagLayout());
GridBagConstraints gbc = new GridBagConstraints();
gbc.fill = GridBagConstraints.BOTH;
gbc.weighty = 1;
textArea = new JTextArea();
textArea.setMargin(new Insets(2, 5, 5, 2));
textArea.setLineWrap(true);
textArea.setWrapStyleWord(true);
textArea.setEditable(false);
scrollPane = new JScrollPane(textArea);
scrollPane.setCursor(Cursor.getPredefinedCursor(Cursor.TEXT_CURSOR));
gbc.weightx = 0.8;
textAndUsers.add(scrollPane, gbc);
list = new FriendsList(listUpdate);
gbc.weightx = 0.2;
textAndUsers.add(list.frmUserList, gbc);
stretchyPanel.add(textAndUsers);
FriendsList is a JList contained in a JPanel.
There are other buttons and text fields in the main CardLayout content pane, but those shouldn't affect what is inside of this GridBagLayout, correct?
I made another copy of this JPanel as a standalone application, and it displays and resizes perfectly. See here:
JFrame frame = new JFrame();
frame.getContentPane().setLayout((new BoxLayout(frame.getContentPane(), BoxLayout.Y_AXIS)));
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setBounds(100, 100, 550, 600);
JPanel stretchyPane = new JPanel();
frame.getContentPane().add(stretchyPane);
stretchyPane.setLayout(new CardLayout(0, 0));
JPanel panel = new JPanel(new GridBagLayout());
GridBagConstraints c = new GridBagConstraints();
JTextArea text = new JTextArea();
text.setMargin(new Insets(2, 5, 5, 2));
JScrollPane panel1 = new JScrollPane(text);
FriendsList panel2 = new FriendsList(new Object());
c.fill = GridBagConstraints.BOTH;
c.weightx = .8;
c.weighty = 1;
panel.add(panel1, c);
c.weightx = .2;
//c.fill = GridBagConstraints.HORIZONTAL;
panel.add(panel2.frmUserList, c);
stretchyPane.add(panel);
frame.setVisible(true);
What could be causing the difference between the two, since I've replicated my original line by line into the copy?
The weightx and weighty properties might appear to act as proportional sizes, but that is not what they do. In fact they determine the distribution of extra space in the layout.
If you set everything to its preferred size by calling pack() on your JFrame, there will be no extra space. Which means the weightx and weighty properties have no effect while it's in that state.
Once the user starts resizing the window to be larger, there will be extra space, and only then will GridBagLayout consult the weightx and weighty properties to determine how to apportion that extra space to each column and row. Until then, it's entirely possible for a component with a small weightx to be wider than a component with a larger weightx, if their preferred sizes dictate it.
Hopefully this simple program will demonstrate this concept. Try using the mouse (or keyboard) to resize the window to be wider, and observe how each of the textfields grows:
import java.awt.EventQueue;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JTextField;
public class GridBagProportions {
static void buildAndShowWindow() {
JTextField small = new JTextField("small (0.8)", 5);
JTextField large = new JTextField("LARGE (0.2)", 30);
small.setMinimumSize(small.getPreferredSize());
large.setMinimumSize(large.getPreferredSize());
JPanel panel = new JPanel(new GridBagLayout());
GridBagConstraints gbc = new GridBagConstraints();
gbc.fill = GridBagConstraints.HORIZONTAL;
gbc.insets.left = 6;
gbc.insets.top = 6;
gbc.insets.bottom = 6;
gbc.weightx = 0.8;
panel.add(small, gbc);
gbc.weightx = 0.2;
gbc.insets.right = 6;
panel.add(large, gbc);
JFrame frame = new JFrame("GridBagLayout Proportions");
frame.getContentPane().add(panel);
frame.pack();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setLocationByPlatform(true);
frame.setVisible(true);
}
public static void main(String[] args) {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
buildAndShowWindow();
}
});
}
}
So what can be done about it? Well, this is one layout scenario that GridBagLayout cannot do. I would try using a SpringLayout:
SpringLayout layout = new SpringLayout();
JPanel textAndUsers = new JPanel(layout);
SpringLayout.Constraints scrollPaneConstraints =
new SpringLayout.Constraints(scrollPane);
Spring scrollPaneWidth = scrollPaneConstraints.getWidth();
SpringLayout.Constraints listConstraints =
new SpringLayout.Constraints(scrollPaneWidth,
scrollPaneConstraints.getY(),
Spring.scale(scrollPaneWidth, 0.25f),
scrollPaneConstraints.getHeight());
layout.putConstraint(SpringLayout.EAST, textAndUsers, 0,
SpringLayout.EAST, frmUserList);
layout.putConstraint(SpringLayout.SOUTH, textAndUsers, 0,
SpringLayout.SOUTH, scrollPane);
textAndUsers.add(scrollPane, scrollPaneConstraints);
textAndUsers.add(frmUserList, listConstraints);
Notice that the creation of listConstraints specifies a width argument which is Spring.scale(scrollPaneWidth, 0.25f). This ensures the list is always one-fourth as wide as the scrollPane containing the JTextArea.
SpringLayout is tricky to use, in that you have to make sure to link the far edges of the layout container to child components explicitly, because SpringLayout won't grow to accommodate all the child components automatically. That's what the putConstraint calls are doing.

Difficulties in aligning ScrollPane in top-left corner

The question is, how do I align JScrollPane in resizeble JPane so that it aligns to the top-left corner instead of a center?
That feels like obvious task, yet I spent hours trying out all the different layouts and properties.
The frustration can be expressed, as there are only two options I could produce:
Use the layout which respects children's maximum preferred size, FlowLayout as example. Side effect - ScrollPane starts behaving as if it was plain Panel:
Use stretchable layout, for instance BorderLayout and place the element into BorderLayout.CENTER (BorderLayout.PAGE_START leads to 1.). I lose control on the location of Panel, and it in the center:
When window is small, scroll works as expected though:
Is it possible to have both of the two worlds: have JScrollPane not stretch beyound maximum preferred size, yet not lose the Scroll?
In case someone needs the source code:
import java.awt.BorderLayout;
import java.awt.Dimension;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import javax.swing.JComponent;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTabbedPane;
public class MainPanelTest extends JFrame {
public static void main(String[] args) {
new MainPanelTest().setVisible(true);
}
public MainPanelTest() {
super();
this.setLayout(new BorderLayout());
setupGUI();
}
private void setupGUI() {
JTabbedPane tabbedPane = new JTabbedPane();
tabbedPane.setTabLayoutPolicy(JTabbedPane.SCROLL_TAB_LAYOUT);
this.add(tabbedPane, BorderLayout.CENTER);
JComponent filesPanel = setupFilesPanel();
tabbedPane.addTab("Files", filesPanel);
JPanel secondPanel = new JPanel();
tabbedPane.addTab("Placeholder", secondPanel);
}
private JComponent setupFilesPanel() {
JPanel filesPanel = new JPanel();
filesPanel.setLayout(new GridBagLayout());
GridBagConstraints c = new GridBagConstraints();
c.anchor = GridBagConstraints.NORTHWEST;
for(int i=0; i<15; i++) {
c.gridy = i;
filesPanel.add(new JLabel("Test row " + i), c);
}
JScrollPane scrollFilesPane = new JScrollPane(filesPanel);
scrollFilesPane.setMaximumSize(scrollFilesPane.getPreferredSize());
return scrollFilesPane;
}
}
The GridBagLayout divides the space into logical cells in which the components live. The anchor is only relevant if the logical cell is bigger than its component. This may happen if components within the same row/column demand a bigger size or if there is extra space and the associated weightx or weighty value is not zero. Since your desired behavior is about the extra space, you have to set the weight values accordingly. The weightx will be non-zero for the sole column but the weighty will be non-zero for the last row only to take up the entire space below it:
GridBagLayout gridBagLayout = new GridBagLayout();
JPanel filesPanel = new JPanel(gridBagLayout);
GridBagConstraints c = new GridBagConstraints();
c.gridx = GridBagConstraints.REMAINDER;
c.anchor = GridBagConstraints.NORTHWEST;
c.weightx = 1; // for the column
for(int i=0; i<15; i++)
filesPanel.add(new JLabel("Test row " + i), c);
// now change the last row:
c.weighty = 1;
gridBagLayout.setConstraints(
filesPanel.getComponent(filesPanel.getComponentCount()-1), c);
Alternatively you can leave the cells unmodified but manipulate the entire layout by adding an empty extra row and column consuming the additional space which will effectively move the original rows and columns to the upper left corner:
// add all visible components which should not grow
GridBagLayout gridBagLayout = new GridBagLayout();
JPanel filesPanel = new JPanel(gridBagLayout);
GridBagConstraints c = new GridBagConstraints();
c.gridx = GridBagConstraints.REMAINDER;
for(int i=0; i<15; i++)
filesPanel.add(new JLabel("Test row " + i), c);
// add an extra row consuming vertical extra space
int nRows=filesPanel.getComponentCount();
gridBagLayout.rowHeights=new int[nRows+1];
gridBagLayout.rowWeights=new double[nRows+1];
gridBagLayout.rowWeights[nRows]=1;
// add an extra column consuming extra horizontal space
gridBagLayout.columnWidths=new int[] { 0, 0 };
gridBagLayout.columnWeights=new double[] { 0, 1 };
See if this is what you are trying to achieve:
private JComponent setupFilesPanel() {
JPanel filesPanel = new JPanel();
filesPanel.setLayout(new GridBagLayout());
GridBagConstraints c = new GridBagConstraints();
c.anchor = GridBagConstraints.NORTHWEST;
for(int i=0; i<15; i++) {
c.gridy = i;
filesPanel.add(new JLabel("Test row " + i), c);
}
// ---- Add this
JPanel newPanel = new JPanel(new GridBagLayout());
c.weightx = 1;
c.weighty = 1;
newPanel.setBackground(Color.yellow);
newPanel.add(filesPanel, c);
// ----
// ---- change the panel you pass to JScrollPanel constructor:
JScrollPane scrollFilesPane = new JScrollPane(newPanel); // <--------- newPanel
scrollFilesPane.setMaximumSize(scrollFilesPane.getPreferredSize());
return scrollFilesPane;
}
The changes are commented.

Categories