Problems with GridBagLayout - java

I cant transfer any of the relatet posts to my simple problem.
In order to get into GridBagLayout i wrote a simple example:
JFrame frame = new JFrame();
JPanel main =new JPanel();
frame.setContentPane(main);
GridBagLayout gbl=new GridBagLayout();
GridBagConstraints gbc = new GridBagConstraints();
main.setLayout(gbl);
JButton btn1 = new JButton("1");
JButton btn2 = new JButton("2");
gbc.gridx=0;
gbc.gridy=0;
gbc.gridheight=1;
gbc.gridwidth=1;
gbc.fill=GridBagConstraints.WEST;
gbl.setConstraints(btn1, gbc);
gbc.gridx=0;
gbc.gridy=1;
gbl.setConstraints(btn2, gbc);
frame.setSize(new Dimension(200,100));
main.add(btn1);
main.add(btn2);
frame.setVisible(true);
Here i have the Problem that neither .fill nor any other parameter of GBConstrains sems to work. I ever get the two buttons in the middle of the window.
thx in advance

Your configuration of your GridBagConstraint are incorrect:
fill can only take: NONE, VERTICAL, HORIZONTAL or BOTH.
anchor can use relative location within a "cell"
fill and anchor almost always require to use weightx/weighty
Try to avoid using gridx and gridy because they get hard to maintain. Instead use their default value (RELATIVE) and you can change the value of gridwidth/gridheight to set it to 1 (or more if needed) and REMAINDER to make the layout wrap to next line/column (components are then positionned in the order they were added to the container.
Here is a working code of yours (although I am not sure of the exact layout you were targetting):
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
public class TestGBL {
protected void initUI() {
JFrame frame = new JFrame();
GridBagLayout gbl = new GridBagLayout();
JPanel main = new JPanel(gbl);
frame.setContentPane(main);
GridBagConstraints gbc = new GridBagConstraints();
gbc.anchor = GridBagConstraints.WEST;
gbc.weightx = 1.0;
main.setLayout(gbl);
JButton btn1 = new JButton("1");
JButton btn2 = new JButton("2");
gbl.setConstraints(btn1, gbc);
gbl.setConstraints(btn2, gbc);
main.add(btn1);
main.add(btn2);
frame.pack();
frame.setVisible(true);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
new TestGBL().initUI();
}
});
}
}

Edit: I made a sample application that demonstrates how to use GridBadLayout that is reusing the constraints:
import java.awt.EventQueue;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.Insets;
import javax.swing.JButton;
import javax.swing.JFrame;
public class Test1 {
private JFrame frame;
/**
* Launch the application.
*/
public static void main(String[] args) {
EventQueue.invokeLater(new Runnable() {
public void run() {
try {
Test1 window = new Test1();
window.frame.setVisible(true);
} catch (Exception e) {
e.printStackTrace();
}
}
});
}
/**
* Create the application.
*/
public Test1() {
initialize();
}
/**
* Initialize the contents of the frame.
*/
private void initialize() {
frame = new JFrame();
frame.setBounds(100, 100, 450, 300);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
GridBagLayout gridBagLayout = new GridBagLayout();
gridBagLayout.columnWidths = new int[]{0, 0, 0};
gridBagLayout.rowHeights = new int[]{0, 0};
gridBagLayout.columnWeights = new double[]{0.0, 0.0, Double.MIN_VALUE};
gridBagLayout.rowWeights = new double[]{0.0, Double.MIN_VALUE};
frame.getContentPane().setLayout(gridBagLayout);
JButton btnTest = new JButton("Test1");
GridBagConstraints gbc_btnTest = new GridBagConstraints();
gbc_btnTest.insets = new Insets(0, 0, 0, 5);
gbc_btnTest.gridx = 0;
gbc_btnTest.gridy = 0;
frame.getContentPane().add(btnTest, gbc_btnTest);
JButton btnTest_1 = new JButton("Test2");
gbc_btnTest.gridx = 1;
gbc_btnTest.gridy = 0;
frame.getContentPane().add(btnTest_1, gbc_btnTest);
}
}

Related

In Java, is there a way to center a group of buttons with variable size(Using Swing)?

I've been trying to make a GUI like this:
However, the difficult I've run into is that I need to make the text of the buttons variable- each button should present a different option, which can vary in text length. While this itself is not difficult, I've tried countless different things but I can't get the buttons to center despite their text length. Whatever I try, I always have one of these problems:
The buttons don't center (there's space left at the left but they exceed the window at the right)
For some reason, the Question and the Goodratio change location based on the button size
I have no clue left how to do this. Does anyone have some useful intuitions on what's the best way to do this? It would be much appreciated.
EDIT: Code of the constructor function(which doesn't center correctly), which I use to set the components, as requested:
public ButtonPannel(Test test)
{
super();
op1Button = new JButton("Option 1");
op2Button = new JButton("Option 2");
op3Button = new JButton("Option 3");
questionText = new JLabel("Question");
rightAndWrongAmount = new JLabel("rightAndWrongAmount");
GridBagLayout gridBagLayout = new GridBagLayout();
gridBagLayout.columnWidths = new int[]{30, 331, 0};
gridBagLayout.rowHeights = new int[]{16, 0, 134, 35, 16, 0};
gridBagLayout.columnWeights = new double[]{0.0, 0.0, Double.MIN_VALUE};
gridBagLayout.rowWeights = new double[]{0.0, 0.0, 1.0, 0.0, 0.0, Double.MIN_VALUE};
setLayout(gridBagLayout);
JPanel panel = new JPanel();
FlowLayout flowLayout = (FlowLayout) panel.getLayout();
op1Button.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
}
});
GridBagConstraints gbc_lblQuestion = new GridBagConstraints();
gbc_lblQuestion.insets = new Insets(0, 0, 5, 0);
gbc_lblQuestion.gridx = 1;
gbc_lblQuestion.gridy = 1;
add(questionText, gbc_lblQuestion);
panel.add(op1Button);
panel.add(op2Button);
panel.add(op3Button);
GridBagConstraints gbc_panel = new GridBagConstraints();
gbc_panel.insets = new Insets(0, 0, 5, 0);
gbc_panel.gridx = 1;
gbc_panel.gridy = 3;
add(panel, gbc_panel);
GridBagConstraints gbc_lblNewLabel_1 = new GridBagConstraints();
gbc_lblNewLabel_1.gridx = 1;
gbc_lblNewLabel_1.gridy = 4;
add(rightAndWrongAmount, gbc_lblNewLabel_1);
op1Button.addActionListener(new InputHandler(test));
op1Button.setActionCommand("1");
op2Button.addActionListener(new InputHandler(test));
op2Button.setActionCommand("2");
op3Button.addActionListener(new InputHandler(test));
op3Button.setActionCommand("3");
}
The code is generated using WindowBuilder. The problem isn't so much in the code, but more which Layout to use etc.
Layouts are a little bit of black magic and trial and error. Very rarely will a single layout achieve exactly what you want, so you often need to resort to compound layouts (using multiple containers and layouts) to get what you want...
So, basically, this is made of a panel, with another panel holding the buttons (and another panel acting as the "pseudo" content to add space)
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.GridLayout;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
public class Test {
public static void main(String[] args) {
new Test();
}
public Test() {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
ex.printStackTrace();
}
JFrame frame = new JFrame("Testing");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(new TestPane());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
public class TestPane extends JPanel {
public TestPane() {
setLayout(new GridBagLayout());
GridBagConstraints gbc = new GridBagConstraints();
gbc.gridwidth = GridBagConstraints.REMAINDER;
add(new JLabel("Question"), gbc);
gbc.fill = GridBagConstraints.BOTH;
gbc.weightx = 1;
gbc.weighty = 1;
// This is just to act as some psudo context
add(new FillerPane(), gbc);
gbc.fill = GridBagConstraints.NONE;
gbc.weightx = 0;
gbc.weighty = 0;
add(makeButtonPane(), gbc);
add(new JLabel("Goodratio"), gbc);
}
public JPanel makeButtonPane() {
JPanel panel = new JPanel(new GridBagLayout());
GridBagConstraints gbc = new GridBagConstraints();
gbc.weightx = 1;
panel.add(new JButton("Short"));
panel.add(new JButton("Long, long, long and lobng"));
panel.add(new JButton("In the middle"));
return panel;
}
}
public class FillerPane extends JPanel {
#Override
public Dimension getPreferredSize() {
return new Dimension(200, 200);
}
}
}
"But I want the buttons to be the same size" I hear you ask, sure, change layouts...
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.GridLayout;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
public class Test {
public static void main(String[] args) {
new Test();
}
public Test() {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
ex.printStackTrace();
}
JFrame frame = new JFrame("Testing");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(new TestPane());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
public class TestPane extends JPanel {
public TestPane() {
setLayout(new GridBagLayout());
GridBagConstraints gbc = new GridBagConstraints();
gbc.gridwidth = GridBagConstraints.REMAINDER;
add(new JLabel("Question"), gbc);
gbc.fill = GridBagConstraints.BOTH;
gbc.weightx = 1;
gbc.weighty = 1;
// This is just to act as some psudo context
add(new FillerPane(), gbc);
gbc.fill = GridBagConstraints.NONE;
gbc.weightx = 0;
gbc.weighty = 0;
add(makeButtonPane(), gbc);
add(new JLabel("Goodratio"), gbc);
}
public JPanel makeButtonPane() {
JPanel panel = new JPanel(new GridLayout(1, 0));
panel.add(new JButton("Short"));
panel.add(new JButton("Long, long, long and lobng"));
panel.add(new JButton("In the middle"));
return panel;
}
}
public class FillerPane extends JPanel {
#Override
public Dimension getPreferredSize() {
return new Dimension(200, 200);
}
}
}
You have to look at the layout and think about how each element relates to the others, in terms of what you want them to do, then find the layout managers which are going to help you achieve the end results and combine them together

Use a JButton to add new panels at runtime

I feel as beginner I may have bitten off too much in regards to application building. That said, I am working on developing an application for a friend that will have prompts where each JPanel will provide fields to create an object to be used later. What I would like to have happen is that when the panel loads, it displays one object creation panel and a button to dynamically add a new panel if the user wants to make multiples (the plus button would add the new panel).
I have drawn up something in paint to illustrate this:
By my very limited understanding, I can create a panel to hold these sub-panels, and then add a action listener to the '+' button to create new panels. The only way I could think to implement this is to create a constructor for the panel I want to add. Is this possible? Let me show you what I have:
package com.company;
import java.awt.*;
import javax.swing.*;
/**
* Created by Travis on 3/1/2015.
*/
public class MainSnakeGui extends JFrame{
protected int panelCount;
//row 1
JPanel row1 = new JPanel();
JLabel splitSnakeLabel = new JLabel("Create a Split Snake", JLabel.CENTER);
//row 2
JPanel row2 = new JPanel();
JButton addButton = new JButton("+");
public MainSnakeGui() {
super("Snake Channels");
setSize(550, 400);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
GridLayout layout = new GridLayout(5, 1, 10, 10);
setLayout(layout);
FlowLayout layout1 = new FlowLayout(FlowLayout.CENTER, 10, 10);
row1.setLayout(layout1);
row1.add(splitSnakeLabel);
add(row1);
GridLayout layout2 = new GridLayout(1, 2, 10, 10);
row2.setLayout(layout2);
row2.add(addButton);
MainSnakeConstructor snakePanel = new MainSnakeConstructor();
row2.add(snakePanel);
add(row2);
setVisible(true);
}
public static void setLookAndFeel () {
try {
UIManager.setLookAndFeel("com.sun.java.swing.plaf.nimbus.NimbusLookAndFeel");
} catch (Exception e) {
}
}
public static void main(String[] arg) {
MainSnakeGui.setLookAndFeel();
MainSnakeGui frame = new MainSnakeGui();
}
}
Here is the constructor:
package com.company;
import javax.swing.*;
import java.awt.*;
/**
* Created by Travis on 3/1/2015.
*/
public class MainSnakeConstructor extends JFrame {
public MainSnakeConstructor () {
JPanel splitSnakeRow = new JPanel();
JLabel snakeNameLabel = new JLabel("Snake Name");
JLabel channelCountLabel = new JLabel("Channel Count");
JCheckBox artistSuppliedCheckBox = new JCheckBox("Artist Supplied?");
JTextField snakeNameTextField = new JTextField(30);
JTextField channelCountTextField = new JTextField(3);
GridLayout layout = new GridLayout(3,2,10,10);
splitSnakeRow.setLayout(layout);
splitSnakeRow.add(snakeNameLabel);
splitSnakeRow.add(channelCountLabel);
splitSnakeRow.add(artistSuppliedCheckBox);
splitSnakeRow.add(snakeNameTextField);
splitSnakeRow.add(channelCountTextField);
add(splitSnakeRow);
}
}
Think about it differently. You want a button that allows you to add new panels, so you really only need a single button.
From there, you need some kind common panel which provides the functionality you want to the user (the creation panel). Then, when the user clicks the add button, you create a new creation panel and add it to the container been used to display them, for example...
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.FlowLayout;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
import javax.swing.border.CompoundBorder;
import javax.swing.border.EmptyBorder;
import javax.swing.border.LineBorder;
public class Test {
public static void main(String[] args) {
new Test();
}
public Test() {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
ex.printStackTrace();
}
JFrame frame = new JFrame("Testing");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(new TestPane());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
public class TestPane extends JPanel {
public TestPane() {
JButton btnAdd = new JButton("+");
setLayout(new BorderLayout());
JPanel buttons = new JPanel(new FlowLayout(FlowLayout.LEFT));
buttons.add(btnAdd);
add(buttons, BorderLayout.NORTH);
JPanel content = new JPanel(new GridBagLayout());
GridBagConstraints gbc = new GridBagConstraints();
gbc.gridwidth = GridBagConstraints.REMAINDER;
gbc.weighty = 1;
content.add(new JPanel(), gbc);
add(new JScrollPane(content));
btnAdd.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
CreationPane pane = new CreationPane();
int insertAt = Math.max(0, content.getComponentCount() - 1);
GridBagConstraints gbc = new GridBagConstraints();
gbc.gridwidth = GridBagConstraints.REMAINDER;
gbc.fill = GridBagConstraints.HORIZONTAL;
gbc.weightx = 1;
content.add(pane, gbc, insertAt);
content.revalidate();
content.repaint();
}
});
}
#Override
public Dimension getPreferredSize() {
return new Dimension(200, 200);
}
}
public static class CreationPane extends JPanel {
private static int count;
public CreationPane() {
setLayout(new GridBagLayout());
add(new JLabel("Make it so " + (count++)));
setBorder(new CompoundBorder(new LineBorder(Color.BLACK), new EmptyBorder(10, 10, 10, 10)));
}
}
}
Now having done all that, I prefer the VerticalLayout manager from SwingLabs, SwingX library, which basically does the same thing...

Align 2 sets/items on either side of center

I'm trying to figure out the best way to align 2 sets of items in the center of a panel in a Java Swing application. The panel is in the North position of a BorderLayout whose width is determined by the JTextField in the Center position of the layout. The problem I'm having is, I have a set of labels and smaller text fields that I want to center so that the end of the label and the start of the first text field meet at the center of the panel.
I've tried GroupLayout, but ended up with the following result:
Note: The 2 text fields separated by a + are in a sub-panel.
What I'm trying to achieve is the following:
Apparently I'm either missing something, or this is far more complicated than necessary to do. I actually run into this issue a LOT! I'm surprised there isn't a special grid layout specifically for this.
Trying to do this with a GridLayout resulted in this:
So... what IS the easiest way to get the layout I'm looking for (second image)?
GroupLayout example code below:
JFrame frame = new JFrame();
JPanel panel = new JPanel(new BorderLayout());
frame.setContentPane(panel);
JPanel longText = new JPanel();
JPanel shortText = new JPanel();
JPanel mediumText = new JPanel();
longText.add(new TextField(5));
longText.add(new JLabel("+"));
longText.add(new TextField(5));
shortText.add(new TextField(5));
shortText.add(new JLabel("+"));
shortText.add(new TextField(5));
mediumText.add(new TextField(5));
mediumText.add(new JLabel("+"));
mediumText.add(new TextField(5));
JLabel lExample = new JLabel("Long text example:");
JLabel sExample = new JLabel("Short:");
JLabel mExample = new JLabel("Medium Example:");
JPanel subPanel = new JPanel();
GroupLayout layout = new GroupLayout(subPanel);
subPanel.setLayout(layout);
layout.setHorizontalGroup(layout.createSequentialGroup()
.addGroup(layout.createParallelGroup(Alignment.CENTER)
.addGroup(layout.createSequentialGroup()
.addGroup(layout.createParallelGroup(Alignment.TRAILING)
.addComponent(lExample)
.addComponent(sExample)
.addComponent(mExample))
.addGroup(layout.createParallelGroup(Alignment.TRAILING)
.addComponent(longText)
.addComponent(shortText)
.addComponent(mediumText))))
.addGroup(layout.createParallelGroup(GroupLayout.Alignment.CENTER))
);
layout.setVerticalGroup(layout.createSequentialGroup()
.addGroup(layout.createParallelGroup(GroupLayout.Alignment.CENTER)
.addComponent(lExample)
.addComponent(longText))
.addGroup(layout.createParallelGroup(GroupLayout.Alignment.CENTER)
.addComponent(sExample)
.addComponent(shortText))
.addGroup(layout.createParallelGroup(GroupLayout.Alignment.CENTER)
.addComponent(mExample).addComponent(mediumText))
);
JTextArea textArea = new JTextArea() {
#Override
public Dimension getPreferredSize() {
return new Dimension(600,300);
}
};
textArea.setBorder(BorderFactory.createEtchedBorder(EtchedBorder.LOWERED));
textArea.setLineWrap(true);
textArea.setWrapStyleWord(true);
textArea.setAutoscrolls(true);
panel.add(subPanel,BorderLayout.NORTH);
panel.add(textArea,BorderLayout.CENTER);
frame.pack();
frame.setVisible(true);
Consider using a GridBagLayout, as it gives you a lot more control over the placement of individual components and respects the preferred size of the components where it can (unless you override them through the use of the GridBagConstraints)
import java.awt.EventQueue;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.Insets;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JTextField;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
public class LayoutExample {
public static void main(String[] args) {
new LayoutExample();
}
public LayoutExample() {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
ex.printStackTrace();
}
JFrame frame = new JFrame("Testing");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(new TestPane());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
public class TestPane extends JPanel {
public TestPane() {
JLabel longText = new JLabel("Long Text Example");
JLabel shortText = new JLabel("Short Example");
JLabel medText = new JLabel("Medium Example");
setLayout(new GridBagLayout());
GridBagConstraints gbc = new GridBagConstraints();
gbc.gridx = 0;
gbc.gridy = 0;
gbc.anchor = GridBagConstraints.EAST;
add(longText, gbc);
addFields(gbc);
gbc.gridx = 0;
gbc.gridy++;
gbc.anchor = GridBagConstraints.EAST;
add(shortText, gbc);
addFields(gbc);
gbc.gridx = 0;
gbc.gridy++;
gbc.anchor = GridBagConstraints.EAST;
add(medText, gbc);
addFields(gbc);
}
protected void addFields(GridBagConstraints gbc) {
JTextField field1 = new JTextField("0", 5);
field1.setEnabled(false);
gbc.anchor = GridBagConstraints.CENTER;
gbc.gridx++;
add(field1, gbc);
gbc.gridx++;
gbc.insets = new Insets(0, 4, 0, 4);
add(new JLabel("+"), gbc);
JTextField field2 = new JTextField(5);
gbc.gridx++;
add(field2, gbc);
}
}
}

What's the best way to organize JPanels in a list?

I've found myself writing up quite a few programs recently which all need to display some collection of data. So far the best looking approach I've thought of is make small JPanels which contain data on each item in the collection and put them all in a big JPanel which I then put in a JScrollPane. It works and looks just as intended but there's one issue: I can't seem to get the smaller JPanels to start at the top of the bigger JPanel.
The problem is only apparent when I've got a small number of small JPanels (green) added into the bigger JPanel (red).
Described below is the method I used to produce the above and I'd like to know if there's a better way I could do it (where the list starts at the top like it should):
I created a class which extends JPanel and in it add all data I want to display. We'll call it "SmallPanel.java". I don't set the size of it (that comes later).
In my main window's class (which extends JFrame):
private JScrollPane scrollPane;
private JPanel panel;
...
scrollPane = new JScrollPane();
getContentPane().add(scrollPane);
panel = new JPanel();
panel.setLayout(new GridBagLayout());
scrollPane.setViewportView(panel);
...
private void addPanel()
{
GridBagConstraints gbc = new GridBagConstraints();
gbc.gridx = 0;
gbc.gridy = panel.getComponentCount(); //The new JPanel's place in the list
gbc.fill = GridBagConstraints.HORIZONTAL;
gbc.anchor = GridBagConstraints.PAGE_START; //I thought this would do it
gbc.ipady = 130; //Set the panel's height, the width will get set to that of the container JPanel (which is what I want since I'd like my JFrames to be resizable)
gbc.insets = new Insets(2, 0, 2, 0); //Separation between JPanels in the list
gbc.weightx = 1.0;
SmallPanel smallPanel = new SmallPanel();
panel.add(smallPanel, gbc);
panel.revalidate();
panel.invalidate();
panel.repaint(); //Better safe than peeved
}
Call the addPanel() method every time I want to add a panel.
EDIT
Final solution (based on MadProgrammer's answer below):
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.Insets;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.border.BevelBorder;
public class ListPanel extends JPanel
{
private static final long serialVersionUID = 1L;
private JPanel fillerPanel;
private ArrayList<JPanel> panels;
public ListPanel(List<JPanel> panels, int height)
{
this(panels, height, new Insets(2, 0, 2, 0));
}
public ListPanel(List<JPanel> panels, int height, Insets insets)
{
this();
for (JPanel panel : panels)
addPanel(panel, height, insets);
}
public ListPanel()
{
super();
this.fillerPanel = new JPanel();
this.fillerPanel.setMinimumSize(new Dimension(0, 0));
this.panels = new ArrayList<JPanel>();
setLayout(new GridBagLayout());
}
public void addPanel(JPanel p, int height)
{
addPanel(p, height, new Insets(2, 0, 2, 0));
}
public void addPanel(JPanel p, int height, Insets insets)
{
super.remove(fillerPanel);
GridBagConstraints gbc = new GridBagConstraints();
gbc.gridx = 0;
gbc.gridy = getComponentCount();
gbc.fill = GridBagConstraints.HORIZONTAL;
gbc.anchor = GridBagConstraints.PAGE_START;
gbc.ipady = height;
gbc.insets = insets;
gbc.weightx = 1.0;
panels.add(p);
add(p, gbc);
gbc = new GridBagConstraints();
gbc.gridx = 0;
gbc.gridy = getComponentCount();
gbc.fill = GridBagConstraints.VERTICAL;
gbc.weighty = 1.0;
add(fillerPanel, gbc);
revalidate();
invalidate();
repaint();
}
public void removePanel(JPanel p)
{
removePanel(panels.indexOf(p));
}
public void removePanel(int i)
{
super.remove(i);
panels.remove(i);
revalidate();
invalidate();
repaint();
}
public ArrayList<JPanel> getPanels()
{
return this.panels;
}
public static void main(String[] args)
{
JFrame f = new JFrame();
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
f.setMinimumSize(new Dimension(500, 500));
f.setLocationRelativeTo(null);
f.getContentPane().setLayout(new BorderLayout());
final ListPanel listPanel = new ListPanel();
for (int i = 1; i <= 10; i++)
listPanel.addPanel(getRandomJPanel(), new Random().nextInt(50) + 50);
JButton btnAdd = new JButton("Add");
btnAdd.addActionListener(new ActionListener()
{
public void actionPerformed(ActionEvent paramActionEvent)
{
listPanel.addPanel(getRandomJPanel(), new Random().nextInt(50) + 50);
}
});
JButton btnRemove = new JButton("Remove");
btnRemove.addActionListener(new ActionListener()
{
public void actionPerformed(ActionEvent paramActionEvent)
{
listPanel.removePanel(0);
}
});
f.getContentPane().add(btnRemove, BorderLayout.NORTH);
f.getContentPane().add(btnAdd, BorderLayout.SOUTH);
JScrollPane scrollPane = new JScrollPane();
scrollPane.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED);
scrollPane.setViewportView(listPanel);
f.getContentPane().add(scrollPane, BorderLayout.CENTER);
f.setVisible(true);
}
public static JPanel getRandomJPanel()
{
JPanel panel = new JPanel();
panel.setBorder(new BevelBorder(BevelBorder.LOWERED, null, null, null, null));
panel.add(new JLabel("This is a randomly sized JPanel"));
panel.setBackground(new Color(new Random().nextFloat(), new Random().nextFloat(), new Random().nextFloat()));
return panel;
}
}
The best solution I've found is to use VerticalLayout from the SwingLabs SwingX (which can be downloaded from here) libraries.
You "could" use a GridBagLayout with an invisible component positioned at the end, whose weighty property is set to 1, but this is a lot more additional work to manage, as you need to keep updating the x/y positions of all the components to keep it in place...
Updated with GridBagLayout example
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.Insets;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
public class VerticalLayoutExample {
public static void main(String[] args) {
new VerticalLayoutExample();
}
public VerticalLayoutExample() {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
}
final TestPane pane = new TestPane();
JButton add = new JButton("Add");
add.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
pane.addAnotherPane();
}
});
JFrame frame = new JFrame("Testing");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setLayout(new BorderLayout());
frame.add(new JScrollPane(pane));
frame.add(add, BorderLayout.SOUTH);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
public class TestPane extends JPanel {
private JPanel filler;
private int y = 0;
public TestPane() {
setBackground(Color.RED);
setLayout(new GridBagLayout());
filler = new JPanel();
filler.setOpaque(false);
GridBagConstraints gbc = new GridBagConstraints();
gbc.weighty = 1;
gbc.gridy = 0;
add(filler, gbc);
}
#Override
public Dimension getPreferredSize() {
return new Dimension(200, 400);
}
public void addAnotherPane() {
JPanel panel = new JPanel(new GridBagLayout());
panel.add(new JLabel("Hello"));
GridBagConstraints gbc = new GridBagConstraints();
gbc.gridy = y++;
gbc.weightx = 1;
gbc.fill = GridBagConstraints.HORIZONTAL;
gbc.gridwidth = GridBagConstraints.REMAINDER;
gbc.insets = new Insets(4, 4, 4, 4);
add(panel, gbc);
GridBagLayout gbl = ((GridBagLayout)getLayout());
gbc = gbl.getConstraints(filler);
gbc.gridy = y++;
gbl.setConstraints(filler, gbc);
revalidate();
repaint();
}
}
}
This is just a concept. As camickr has pointed out, so long as you know the last component, you can adjust the GridBagConstraints of the component so that the last component which is in the list has the weighty of 1 instead...
As you can, you can override some of the things GridBagLayout does, for example, instead of using the preferred size of the panel, I've asked GridBagLayout to make it fill the HORIZONTAL width of the parent container...
You can use a vertical BoxLayout.
Just make sure the maximum size of the panel is equal to the preferred size so the panel doesn't grow.
Edit:
Since your class already has a custom panel all you need to do is override the getMaximumSize() method to return an appropriate value. Something like:
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import javax.swing.border.*;
public class VerticalLayoutExample2 {
public static void main(String[] args) {
new VerticalLayoutExample2();
}
public VerticalLayoutExample2() {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
}
final TestPane pane = new TestPane();
JButton add = new JButton("Add");
add.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
pane.addAnotherPane();
}
});
JFrame frame = new JFrame("Testing");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setLayout(new BorderLayout());
frame.add(new JScrollPane(pane));
frame.add(add, BorderLayout.SOUTH);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
public class TestPane extends JPanel {
private JPanel filler;
private int y = 0;
public TestPane() {
setBackground(Color.RED);
setLayout(new GridBagLayout());
setLayout(new BoxLayout(this, BoxLayout.Y_AXIS));
setBorder( new EmptyBorder(4, 4, 4, 4) );
}
#Override
public Dimension getPreferredSize() {
return new Dimension(200, 400);
}
public void addAnotherPane() {
SmallPanel panel = new SmallPanel();
panel.setLayout( new GridBagLayout() );
panel.add(new JLabel("Hello"));
add(panel);
add(Box.createVerticalStrut(4));
revalidate();
repaint();
}
}
static class SmallPanel extends JPanel
{
#Override
public Dimension getMaximumSize()
{
Dimension preferred = super.getPreferredSize();
Dimension maximum = super.getMaximumSize();
maximum.height = preferred.height;
return maximum;
}
}
}
I know you mentioned you don't want to use a lib, but you can also look at Relative Layout. It is only a single class. It can easily mimic a BoxLayout but is easier to use because you don't need to override the getMaximumSize() method or add a Box component to the panel to give the vertical spacing.
You would set it as the layout of your panel as follow:
RelativeLayout rl = new RelativeLayout(RelativeLayout.Y_AXIS);
rl.setFill( true ); // fills components horizontally
rl.setGap(4); // vertical gap between panels
yourPanel.setLayout(rl);
yourPanel.add( new SmallPanel(...) );
yourPanel.add( new SmallPanel(...) );

Alignment issues with BoxLayout

I am making an application for which I am using a BoxLayout. As you can see in the following picture, when the title string is short, it's perfect. But as the string gets longer, the JLabel gets more and more misaligned.
Here's some code that is related to the problem:
JPanel centerPanel = new JPanel();
centerPanel.setLayout(new BoxLayout(centerPanel, BoxLayout.PAGE_AXIS));
frame.add(centerPanel, BorderLayout.CENTER);
//...
JLabel l = new JLabel(/*...*/);
l.setHorizontalAlignment(SwingConstants.CENTER); //I tried removing and adding
//this but nothing changed
centerPanel.add(l);
Is there something obvious I am missing? Google isn't being helpful with this problem.
In case it's important, the country-label-progress-bar things are just JPanels with FlowLayouts.
SSCCE:
import java.awt.BorderLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.*;
public class SSCCE {
public static void main(String[] args) {
final JFrame f = new JFrame("SSCCE");
JPanel p = new JPanel();
p.setLayout(new BoxLayout(p, BoxLayout.PAGE_AXIS));
final JLabel[] titles = new JLabel[5];
JPanel[] smallPanels = new JPanel[titles.length];
for (int i = 0; i < smallPanels.length; i ++) {
titles[i] = new JLabel(Math.random() < 0.5 ? "foo" : "bar");
p.add(titles[i]);
smallPanels[i] = new JPanel();
smallPanels[i].add(new JLabel("foobar"));
smallPanels[i].add(new JProgressBar());
p.add(smallPanels[i]);
}
f.add(p, BorderLayout.CENTER);
final JTextField tf = new JTextField("foobar");
tf.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
titles[2].setText(tf.getText());
f.repaint();
}
});
f.add(tf, BorderLayout.NORTH);
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
f.setSize(600, 600);
f.setVisible(true);
}
}
To operate the SSCCE, type something in the text field and press enter.
Here is an updated version of your SSCCE with a GridBagLayout. Not sure of how you want components to resize when labels or frame size changes but it should not be too hard to manage this.
import java.awt.BorderLayout;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.Insets;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.Random;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JProgressBar;
import javax.swing.JTextField;
public class SSCCE {
public static void main(String[] args) {
final JFrame f = new JFrame("SSCCE");
JPanel p = new JPanel();
p.setLayout(new GridBagLayout());
Insets insets = new Insets(3, 3, 3, 3);
GridBagConstraints gbc1 = new GridBagConstraints();
gbc1.gridwidth = GridBagConstraints.REMAINDER;
gbc1.anchor = GridBagConstraints.CENTER;
gbc1.insets = insets;
GridBagConstraints gbc2 = new GridBagConstraints();
gbc2.anchor = GridBagConstraints.EAST;
gbc2.insets = insets;
GridBagConstraints gbc3 = new GridBagConstraints();
gbc3.weightx = 1.0;
gbc3.fill = GridBagConstraints.HORIZONTAL;
gbc3.gridwidth = GridBagConstraints.REMAINDER;
gbc3.insets = insets;
final JLabel[] titles = new JLabel[5];
Random random = new Random();
for (int i = 0; i < titles.length; i++) {
titles[i] = new JLabel(Math.random() < 0.5 ? "foo" : "bar");
p.add(titles[i], gbc1);
p.add(new JLabel("foobar"), gbc2);
JProgressBar progress = new JProgressBar();
progress.setStringPainted(true);
progress.setString(String.valueOf(random.nextInt(100)));
p.add(progress, gbc3);
}
f.add(p, BorderLayout.CENTER);
final JTextField tf = new JTextField("foobar");
tf.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
titles[2].setText(tf.getText());
f.repaint();
}
});
f.add(tf, BorderLayout.NORTH);
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
f.pack();
f.setVisible(true);
}
}
BoxLayout accepting Min, Max and PreferredSize, childs could be resizable from Min to MaxSize
FlowLayout accepting only PreferredSize, rest (Min, MaxSize) is ignored by this LayoutManager, childs aren't resizable
these XxxSize are calculated from PreferredSize came from childs placed into container (JPanel in this case)
(your question) for better help sooner post an SSCCE, short, runnable, compilable, just about your issue

Categories

Resources