I have encountered a problem whilst working the JFrame's, and JButtons. I am trying to center my JButton, however when I do so, it covers almost the entire screen. Any idea's on what's causing this?
Here is a picture on how it looks :
And here is my code :
package character;
import javax.imageio.ImageIO;
import javax.swing.*;
import java.awt.*;
import java.io.IOException;
/**
* Created by Niknea on 6/28/14.
*/
public class characterSelector{
JFrame cselectorButtons, clogo;
JLabel logo, characterName, label;
JButton male, female;
public characterSelector(){
this.createCharacterSelector();
}
public void createCharacterSelector() {
try {
label = new JLabel(new ImageIcon(ImageIO.read(getClass()
.getResource("/resources/Grass_Background.jpg"))));
cselectorButtons = new JFrame("SupremeSoccer");
logo = new JLabel(new ImageIcon(ImageIO.read(this.getClass().getResource("/resources/Character_Selector_Image.png"))));
characterName = new JLabel("<Character Name>");
characterName.setFont(new Font(characterName.getFont().getName(),
Font.HANGING_BASELINE, 50));
/*
Buttons
*/
male = new JButton("Male");
////******************////
//// END OF BUTTONS ////
////****************////
cselectorButtons.add(logo);
cselectorButtons.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
cselectorButtons.setContentPane(label);
cselectorButtons.setLayout(new BorderLayout());
cselectorButtons.add(logo, BorderLayout.NORTH);
cselectorButtons.add(male, BorderLayout.CENTER);
cselectorButtons.pack();
cselectorButtons.setLocationRelativeTo(null);
cselectorButtons.setVisible(true);
} catch (IOException exp) {
exp.printStackTrace();
}
}
}
Thanks again.
Any idea's on what's causing this?
This is the default behaviour of BorderLayout. The component at the CENTER position will occupy the maximum amount of space the is available from the parent component, when the other (NORTH, SOUTH, EAST, WEST) positions have been taken into account
Depending on what you are trying to achieve you might consider creating another JPanel (set it's opaque state to false so it's transparent) and use something like GridLayout or GridBagLayout instead.
Take a look at A Visual Guide to Layout Managers for some more ideas
Updated
So based on your linked code, I changed
part2 = new JPanel();
to
part2 = new JPanel(new java.awt.GridBagLayout());
And got...
Updated with additional example
Start by breaking down your requirements into individual containers and focus on the layout requirements for each individual, then build them all back into a single container.
This will make changing them later much easier and also make controlling them much easier...
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.EventQueue;
import java.awt.Font;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.Insets;
import java.awt.image.BufferedImage;
import java.io.IOException;
import javax.imageio.ImageIO;
import javax.swing.ImageIcon;
import javax.swing.JButton;
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 ExampleLayout {
public static void main(String[] args) {
new ExampleLayout();
}
public ExampleLayout() {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
}
JFrame frame = new JFrame("Testing");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setLayout(new BorderLayout());
frame.add(new TestPane());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
public class TestPane extends JPanel {
private HeaderPane header;
private ScorePane score;
private CharacterSelectionPane characterSelection;
public TestPane() {
setLayout(new BorderLayout());
JLabel background = new JLabel();
try {
BufferedImage img = ImageIO.read(getClass().getResource("/Grass.jpg"));
background.setIcon(new ImageIcon(img));
} catch (IOException ex) {
ex.printStackTrace();
}
add(background);
background.setLayout(new GridBagLayout());
header = new HeaderPane();
score = new ScorePane();
characterSelection = new CharacterSelectionPane();
GridBagConstraints gbc = new GridBagConstraints();
gbc.gridwidth = GridBagConstraints.REMAINDER;
gbc.weightx = 1;
gbc.fill = GridBagConstraints.BOTH;
background.add(header, gbc);
background.add(score, gbc);
gbc.weighty = 1;
background.add(characterSelection, gbc);
}
}
public class HeaderPane extends JPanel {
public HeaderPane() {
setLayout(new BorderLayout());
JLabel label = new JLabel("Character Selection");
label.setForeground(Color.WHITE);
label.setFont(label.getFont().deriveFont(Font.BOLD, 48f));
label.setHorizontalAlignment(JLabel.CENTER);
add(label);
setOpaque(false);
}
}
public class ScorePane extends JPanel {
public ScorePane() {
JLabel label = new JLabel("[-][-[]-][-]");
label.setForeground(Color.YELLOW);
add(label);
setOpaque(false);
}
}
public class CharacterSelectionPane extends JPanel {
private JButton btMale;
private JButton btFemale;
private JTextField tfName;
private JButton btContinue;
public CharacterSelectionPane() {
setOpaque(false);
setLayout(new GridBagLayout());
btMale = new JButton("Male");
btFemale = new JButton("Female");
btContinue = new JButton("Continue");
tfName = new JTextField(10);
GridBagConstraints gbc = new GridBagConstraints();
gbc.gridx = 0;
gbc.gridy = 0;
gbc.insets = new Insets(4, 4, 4, 4);
gbc.fill = GridBagConstraints.HORIZONTAL;
add(btMale, gbc);
gbc.gridx++;
add(btFemale, gbc);
gbc.gridx = 0;
gbc.gridy++;
add(new JLabel("Name:"), gbc);
gbc.gridx++;
add(tfName, gbc);
gbc.gridx = 0;
gbc.gridy++;
gbc.gridwidth = GridBagConstraints.REMAINDER;
add(btContinue, gbc);
}
}
}
Use a BoxLayout or (easier) GridBagLayout for the bottom area, as seen in this answer.
Related
Can JPanels be used in Java like <div>s get used in HTML? Is it ever consistent with good practice to have more than 1 JPanel in a JFrame for something other than CardLayout? (For example, if I had JPanel that contained a help/about button that was on the northern border of the content panel of the JFrame, and I used the one that contained the button to put some spacing in to force the button to be of a certain size...)
Specific case:
Suppose I am trying to make this: , and upon starting, I have a mock like this (thanks to some code for the button rendering I found elsewhere): . Would this case be good case to use multiple JFrames (like one would those HTML <div>s)? If not, when would be?
Based on your requirements, you don't "have" to use multiple panels, you can accomplish what you want from a single panel and a GridBagLayout, for example...
import java.awt.EventQueue;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import javax.swing.JButton;
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 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 helpButton = new JButton("?");
JTextField projectDirectory = new JTextField(20);
JTextField documentDirectory = new JTextField(20);
JButton projectButton = new JButton("Project");
JButton documentButton = new JButton("Document");
JButton continueButton = new JButton("Continue to files");
setLayout(new GridBagLayout());
GridBagConstraints gbc = new GridBagConstraints();
gbc.gridx = 0;
gbc.gridy = 1;
add(new JLabel("Project Directory"), gbc);
gbc.gridy++;
add(new JLabel("Documentation Directory"), gbc);
gbc.gridx = 1;
gbc.gridy = 1;
add(projectDirectory, gbc);
gbc.gridy++;
add(documentDirectory, gbc);
gbc.gridx = 2;
gbc.gridy = 0;
add(helpButton, gbc);
gbc.gridy++;
add(projectButton, gbc);
gbc.gridy++;
add(documentButton, gbc);
gbc.gridx = 0;
gbc.gridy++;
gbc.gridwidth = GridBagConstraints.REMAINDER;
add(continueButton, gbc);
}
}
}
The main reasons I would use multiple panels is:
The layout was overly complex, it would allow me to break down the layout into individual requirements and focus more on the relationship between the groups
If I was focusing on separating the management into individual elements (such as the directory selection for example), where the panel could become a self contained unit of work, including the management and functionality, allowing to be a self contained and re-usable component
Compound Panels...
import java.awt.EventQueue;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.Insets;
import javax.swing.JButton;
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 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 helpButton = new JButton("?");
JButton continueButton = new JButton("Continue to files");
setLayout(new GridBagLayout());
GridBagConstraints gbc = new GridBagConstraints();
gbc.gridx = 0;
gbc.gridy = 0;
gbc.anchor = GridBagConstraints.EAST;
add(helpButton, gbc);
gbc.gridy++;
add(new FolderSelectionPane("Project Folder"), gbc);
gbc.gridy++;
add(new FolderSelectionPane("Documentation Folder"), gbc);
gbc.anchor = GridBagConstraints.CENTER;
gbc.gridy++;
add(continueButton, gbc);
}
}
public class FolderSelectionPane extends JPanel {
public FolderSelectionPane(String label) {
JTextField projectDirectory = new JTextField(20);
JButton projectButton = new JButton("Folder");
setLayout(new GridBagLayout());
GridBagConstraints gbc = new GridBagConstraints();
gbc.gridx = 0;
gbc.gridy = 0;
gbc.insets = new Insets(0, 2, 0, 2);
add(new JLabel(label), gbc);
gbc.gridx++;
add(projectDirectory, gbc);
gbc.gridx++;
add(projectButton, gbc);
}
}
}
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
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...
I'm having trouble positioning elements in a JPanel. I tried using GridBagLayout but that doesn't seem to make any of the parts of the panel GUI components move. What should I do?
panel.add(Label);
panel.add(TextField);
panel.add(Label);
panel.add(JChooser);
Nothing seems to help move these GUI elements. They just act like they are in a FlowLayout. What should I do? I'm using a CardLayout for another panel (that panel holds other panels like this one in it), but this panel, I need to align them to the left.
The Label and TextField need to be on the same line, but the Label and JChooser need to be on a different line.
Example:
SomeLabel : [ TextField ]
SomeLabel : [Chooser]
I hope I explained well enough.
Take a closer look at Laying Out Components Within a Container and How to Use GridBagLayout
GridBagLayout requires constraints, which define how and where a component will be positioned within the virtual grid maintained by it
import java.awt.EventQueue;
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.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
public class TestLayout {
public static void main(String[] args) {
new TestLayout();
}
public TestLayout() {
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.gridx = 0;
gbc.gridy = 0;
gbc.anchor = GridBagConstraints.WEST;
gbc.insets = new Insets(4, 4, 4, 4);
add(new JLabel("SomeLabel :"), gbc);
gbc.gridy++;
add(new JLabel("SomeLabel :"), gbc);
gbc.gridx = 1;
gbc.gridy = 0;
gbc.anchor = GridBagConstraints.EAST;
gbc.fill = GridBagConstraints.BOTH;
add(new JTextField(10), gbc);
gbc.gridy++;
add(new JComboBox(new Object[]{"Puppies", "Kittens"}), gbc);
}
}
}
To set elements in JPanel use one of LayoutManagers.
If you want to set components free from location.
then use following to remove layout.
panel.setLayout(null);
After this you can call setBounds method to set bounds for component, or setLocation .
comp.setLocation(int left, int top);
comp.setBounds(int left, int top, int width, int height);
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(...) );