Trying to build out a GUI for my game but no matter what layout I work with I can't get the nest of panels to do what I like
My goal is this
http://i182.photobucket.com/albums/x202/NekoLLX/CharGenmockup-1.jpg
http://i182.photobucket.com/albums/x202/NekoLLX/CharGenmockup2.jpg
And Building off the Mad ones excelent revision I've got my left side as i like it but now the right eludes me
The general idea is that clicking on the title bars of the left menu will colapase (set visible to false) the content panes associated with them
//http://docs.oracle.com/javase/7/docs/api/java/awt/Color.html
//http://stackoverflow.com/questions/16430922/working-with-nested-panels
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.EventQueue;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.GridLayout;
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.EmptyBorder;
import javax.swing.border.LineBorder;
import java.awt.*;
import javax.swing.*;
import javax.swing.text.*;
import java.awt.Graphics;
import java.awt.Point;
import java.awt.event.*;
public class JaGCharCreation {
//set inital size of window
Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize();
int initalWidth = (int) screenSize.width - 50;
int initalHeight = (int) screenSize.height - 50;
public static void main(String[] args) {
new JaGCharCreation ();
}
//set up thread safe invoking for GUI
public JaGCharCreation () {
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);
// Give the frame an initial size.
frame.setSize(initalWidth, initalHeight);
}
});
}
//main panel to hold all others
public class TestPane extends JPanel {
public TestPane() {
setLayout(new GridLayout(0, 2));
add(createLeftPane());
add(createRightPane());
}//end of class for master frame
protected JPanel createLeftPane() {
JLabel CharName = new JLabel("Character Name");
CharName.setFont(new Font("Impact", Font.BOLD, 30));
CharName.setBorder(new EmptyBorder(0, 81,0, 00));
JPanel panel = new JPanel(new BorderLayout());
panel.setBorder(new EmptyBorder(10, 10, 10, 10));
panel.setBackground(Color.RED);
JPanel content = new JPanel(new GridBagLayout());
content.setOpaque(false);
JPanel view3D = new JPanel();
view3D.setBackground(Color.DARK_GRAY);
JPanel view2D = new JPanel();
view2D.setBackground(Color.PINK);
JPanel viewIsometric = new JPanel();
viewIsometric.setBackground(Color.YELLOW);
JPanel viewData = new JPanel();
viewData.setBackground(Color.MAGENTA);
JPanel top = new JPanel(new GridLayout(0, 2));
top.setBorder(new EmptyBorder(0, 80,0, 80));
top.setBackground(Color.GREEN);
top.add(view3D);
top.add(view2D);
JPanel bottom = new JPanel(new GridLayout(2, 0));
bottom.setBorder(new EmptyBorder(0, 80,0, 80));
bottom.setBackground(Color.GREEN);
bottom.add(viewIsometric);
bottom.add(new JScrollPane(viewData));
GridBagConstraints gbc = new GridBagConstraints();
gbc.gridwidth = GridBagConstraints.REMAINDER;
gbc.weighty = 0.5f;
gbc.weightx = 1f;
gbc.fill = GridBagConstraints.BOTH;
content.add(top, gbc);
content.add(bottom, gbc);
panel.add(content);
panel.add(CharName, BorderLayout.NORTH);
return panel;
}//end left pane
protected JPanel createRightPane() {
JPanel panel = new JPanel(new BorderLayout());
panel.setBackground(Color.BLUE);
//set up our image for the title bars
ImageIcon icon = new ImageIcon("GradientDetail.png");
Image img = icon.getImage();
img = img.getScaledInstance(initalWidth/2, 40, java.awt.Image.SCALE_SMOOTH);
final ImageIcon iconSM = new ImageIcon(img);
JPanel name_panel = new JPanel(new BorderLayout())
{
protected void paintComponent(Graphics g)
{
// Dispaly image at full size
g.drawImage(iconSM.getImage(), 0, 0, null);
super.paintComponent(g);
}
};
name_panel.setOpaque( false );
JLabel label = new JLabel(" Character Name");
label.setFont(new Font("Impact", Font.BOLD, 30));
label.setForeground(Color.white);
label.setOpaque(false);
JPanel name_panel_text = new JPanel(new BorderLayout());
name_panel_text.setBackground(Color.WHITE);
name_panel.add(label, BorderLayout.NORTH);
panel.add(name_panel_text);
panel.add(name_panel);
return panel;
}//end right pane
//bassed from http://stackoverflow.com/questions/7340001/determine-clicked-jpanel-component-in-the-mouselistener-event-handling
public class MouseAdapterMod extends MouseAdapter {
// usually better off with mousePressed rather than clicked
public void mousePressed(MouseEvent e) {
if (e.getSource() == "name_panel"){
}
}
}
}//end master panel set
}//end master class
Something along the lines of ...
To be honest, I tried following your code, but got lost, so I re-wrote it...
Basically, you relying on setSize which is going to be ignored and changed by the layout managers as they see fit.
This example uses GridBagLayout and weighty to adjust the space allocated to the top (2D/3D) views and the bottom views, but you should also take a look at overriding the getPreferredSize of the final components, to provide better hints to the layout managers.
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.EventQueue;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.GridLayout;
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.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) {
}
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 {
public TestPane() {
setLayout(new GridLayout(0, 2));
add(createLeftPane());
add(createRightPane());
}
protected JPanel createLeftPane() {
JPanel panel = new JPanel(new BorderLayout());
panel.setBorder(new EmptyBorder(10, 10, 10, 10));
panel.setBackground(Color.RED);
JPanel content = new JPanel(new GridBagLayout());
content.setOpaque(false);
JPanel view3D = new JPanel();
view3D.setBackground(Color.DARK_GRAY);
JPanel view2D = new JPanel();
view2D.setBackground(Color.PINK);
JPanel viewIsometric = new JPanel();
viewIsometric.setBackground(Color.YELLOW);
JPanel viewData = new JPanel();
viewData.setBackground(Color.MAGENTA);
JPanel top = new JPanel(new GridLayout(0, 2));
top.setBorder(new LineBorder(Color.GREEN, 2));
top.add(view3D);
top.add(view2D);
JPanel bottom = new JPanel(new GridLayout(2, 0));
bottom.add(viewIsometric);
bottom.add(new JScrollPane(viewData));
GridBagConstraints gbc = new GridBagConstraints();
gbc.gridwidth = GridBagConstraints.REMAINDER;
gbc.weighty = 0.5f;
gbc.weightx = 1f;
gbc.fill = GridBagConstraints.BOTH;
content.add(top, gbc);
content.add(bottom, gbc);
panel.add(content);
panel.add(new JLabel("Character name"), BorderLayout.NORTH);
return panel;
}
protected JPanel createRightPane() {
JPanel panel = new JPanel();
panel.setBackground(Color.BLUE);
return panel;
}
}
}
Related
I have a problem related to JList. I'm getting the following layout.
JTextField | Button 1 | Button 2
Button 3 | Button 4 | Button 5
This is the layout I'm getting:
JTextField | Button 1 | Button 2
Button 3 | Button 4 | Button 5
As you can see, I do not have JList in between. I did try inserting a list component there but when I did it, the GridLayout would push the list beside button 2. I only want the list to be my itself in between those.
package ManyThings;
import java.awt.GridLayout;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JList;
import javax.swing.JPanel;
javax.swing.JTextField;
public class test {
public static void main(String[] args) {
JFrame.setDefaultLookAndFeelDecorated(true);
JFrame frame = new JFrame("GridLayout Test");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setLayout(new GridLayout(1, 3));
JPanel p1 = new JPanel();
p1.setLayout(new GridLayout(3,1));
JTextField t1;
t1=new JTextField();
p1.add(new JTextField());
p1.add(new JButton("Search"));
p1.add(new JButton("Clear"));
frame.add(p1);
JPanel p2 = new JPanel();
p2.setLayout(new GridLayout(1,3));
p2.add(new JButton("Add"));
p2.add(new JButton("Detail"));
p2.add(new JButton("Remove"));
frame.add(p2);
frame.pack();
frame.setVisible(true);
}
}
Expecting the following layout.
JTextField | Button 1 | Button 2
JList
Button 3 | Button 4 | Button 5
Compound layouts
Make use of multiple containers, using different layout managers (as required)
import java.awt.EventQueue;
import java.awt.GridLayout;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JList;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTextField;
public class Test {
public static void main(String args[]) {
new Test();
}
public Test() {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
JFrame frame = new JFrame();
frame.add(new TestPane());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
public class TestPane extends JPanel {
public TestPane() {
setLayout(new GridLayout(3, 1));
JPanel topRow = new JPanel(new GridLayout(1, 3));
topRow.add(new JTextField(10));
topRow.add(new JButton("Search"));
topRow.add(new JButton("Clear"));
add(topRow);
add(new JScrollPane(new JList()));
JPanel bottomRow = new JPanel(new GridLayout(1, 3));
bottomRow.add(new JButton("Add"));
bottomRow.add(new JButton("Detail"));
bottomRow.add(new JButton("Remove"));
add(bottomRow);
}
}
}
Different layout managers
Use different layout managers to better meet the requirements of the individual components
import java.awt.EventQueue;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.GridLayout;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JList;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTextField;
public class Test {
public static void main(String args[]) {
new Test();
}
public Test() {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
JFrame frame = new JFrame();
frame.add(new TestPane());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
public class TestPane extends JPanel {
public TestPane() {
setLayout(new GridBagLayout());
JPanel topRow = new JPanel(new GridLayout(1, 3));
topRow.add(new JTextField(10));
topRow.add(new JButton("Search"));
topRow.add(new JButton("Clear"));
JPanel bottomRow = new JPanel(new GridLayout(1, 3));
bottomRow.add(new JButton("Add"));
bottomRow.add(new JButton("Detail"));
bottomRow.add(new JButton("Remove"));
GridBagConstraints gbc = new GridBagConstraints();
gbc.weightx = 1;
gbc.fill = GridBagConstraints.HORIZONTAL;
gbc.gridx = 0;
gbc.gridy = 0;
add(topRow, gbc);
gbc.gridy = 2;
add(bottomRow, gbc);
gbc.gridy = 1;
gbc.weighty = 1;
gbc.fill = GridBagConstraints.BOTH;
add(new JScrollPane(new JList()), gbc);
}
}
}
See Laying Out Components Within a Container for more details
I have a button panel and I want a space of 25px between the buttons and a right margin of 5px and a left margin of 5px (the button at the right would be at 5 pixels of the window border).
Flow layout set a gap of the same size everywhere. Gridlayout permit to do that, but then all the buttons have the same size and it is no that I want. The only solution I found is to set Flow layout with hgap=0. then I had an emptyMargin and I but a rigid area before each button, but I think this solution is a bad practice.
What is the best solution to do that ?
import java.awt.BorderLayout;
import java.awt.Container;
import java.awt.FlowLayout;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
public class FlowLayoutDemo extends JFrame{
FlowLayout experimentLayout = new FlowLayout(FlowLayout.LEFT, 25, 0);
public FlowLayoutDemo(String name) {
super(name);
}
public void addComponentsToPane(final Container pane) {
final JPanel compsToExperiment = new JPanel();
compsToExperiment.setLayout(experimentLayout);
experimentLayout.setAlignment(FlowLayout.TRAILING);
compsToExperiment.add(new JButton("Button 1"));
compsToExperiment.add(new JButton("Button 2"));
compsToExperiment.add(new JButton("Button 3"));
compsToExperiment.add(new JButton("Long-Named Button 4"));
compsToExperiment.add(new JButton("5"));
pane.add(compsToExperiment, BorderLayout.CENTER);
}
private static void createAndShowGUI() {
FlowLayoutDemo frame = new FlowLayoutDemo("FlowLayoutDemo");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.addComponentsToPane(frame.getContentPane());
frame.pack();
frame.setVisible(true);
}
public static void main(String[] args) {
javax.swing.SwingUtilities.invokeLater(new Runnable() {
public void run() {
createAndShowGUI();
}
});
}
}
Consider using a GridBagLayout instead, it provides a greater amount of control and customisation.
See How to use GridBagLayout for more details.
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.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.gridy = 0;
gbc.insets = new Insets(5, 5, 5, 25);
gbc.fill = gbc.HORIZONTAL;
gbc.weightx = 1;
add(new JButton("Button 1"), gbc);
gbc.insets = new Insets(5, 0, 5, 25);
add(new JButton("Button 2"), gbc);
add(new JButton("Button 3"), gbc);
add(new JButton("Long-Named Button 4"), gbc);
gbc.insets = new Insets(5, 0, 5, 5);
add(new JButton("5"), gbc);
}
}
}
Note, the example forces the buttons too occupy ALL of the available space. If this doesn't meet your needs in particular, try playing around with the fill and weightx values
The best way is to use the GridBagLayout as it shown in example of MadProgrammer. But it also possible using FlowLayout. Here is the example:
import java.awt.BorderLayout;
import java.awt.Container;
import java.awt.FlowLayout;
import javax.swing.Box;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
public class FlowLayoutDemo extends JFrame {
FlowLayout experimentLayout = new FlowLayout(FlowLayout.LEFT, 5, 0);
public FlowLayoutDemo(String name) {
super(name);
}
public void addComponentsToPane(final Container pane) {
final JPanel compsToExperiment = new JPanel();
compsToExperiment.setLayout(experimentLayout);
experimentLayout.setAlignment(FlowLayout.TRAILING);
compsToExperiment.add(new JButton("Button 1"));
compsToExperiment.add(Box.createHorizontalStrut(20));
compsToExperiment.add(new JButton("Button 2"));
compsToExperiment.add(Box.createHorizontalStrut(20));
compsToExperiment.add(new JButton("Button 3"));
compsToExperiment.add(Box.createHorizontalStrut(20));
compsToExperiment.add(new JButton("Long-Named Button 4"));
compsToExperiment.add(Box.createHorizontalStrut(20));
compsToExperiment.add(new JButton("5"));
pane.add(compsToExperiment, BorderLayout.CENTER);
}
private static void createAndShowGUI() {
FlowLayoutDemo frame = new FlowLayoutDemo("FlowLayoutDemo");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.addComponentsToPane(frame.getContentPane());
frame.pack();
frame.setVisible(true);
}
public static void main(String[] args) {
javax.swing.SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
createAndShowGUI();
}
});
}
}
If you want to get tricky so you don't need to play with all the constraints of GridBagLayout or add filler components you could do:
JPanel compsToExperiment = new JPanel(experimentLayout);
compsToExperiment.setBorder( new EmptyBorder(0, -20, 0, -20) );
This effectively decrease the space by 20 pixels around the left/right edges of the panel.
Also, just wanted to point out that you have:
FlowLayout experimentLayout = new FlowLayout(FlowLayout.LEFT, 25, 0);
...
experimentLayout.setAlignment(FlowLayout.TRAILING);
You set the layout to left aligned and then change it to trailing. This is a little confusing. You can just set it to trailing when you create the layout:
FlowLayout experimentLayout = new FlowLayout(FlowLayout.TRAILING, 25, 0);
I have a simple GUI and I just want to have some text fields stacked on top of each other, with each box being a JPanel. I'm currently using FlowLayout for both the JFrame and JPanel but it changes from being stacked to being in a row when I maximize. Ideally I'd like the text fields to stay in the center of the window even if its maximized. I was looking at using a BoxLayout but was having some trouble getting that to work.
public static Component textbox(String x){
JPanel panel = new JPanel(new FlowLayout(5,5,5));
JLabel lbltAm= new JLabel(x);
JTextField tftAm = new JTextField(10);
lbltAm.setFont(new Font("Serif", Font.PLAIN, 14));
lbltAm.setForeground(Color.white);
panel.add(lbltAm, BorderLayout.NORTH);
panel.add(tftAm, BorderLayout.CENTER);
panel.setBackground(Color.DARK_GRAY);
Border lowerbevel = BorderFactory.createBevelBorder(BevelBorder.LOWERED);
panel.setBorder(lowerbevel);
return panel;
}
private static void createAndShowGUI() {
//Create and set up the window.
JFrame frame = new JFrame("Tip Calculator");
//Add Textbox
frame.setLayout(new BoxLayout(frame, BoxLayout.Y_AXIS));
String Label = "Tip Calculator";
JLabel header = new JLabel(Label);
header.setFont(new Font("Serif", Font.BOLD, 18));
frame.add(header);
frame.add(textbox("Total"));
frame.add(textbox("Tip %"));
frame.add(textbox("People"));
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.getContentPane().setBackground(Color.gray);
frame.setPreferredSize(new Dimension(300, 400));
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
It's a matter of opinion, but for flexibility, I prefer to use GridBagLayout
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Component;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Font;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.Insets;
import javax.swing.BorderFactory;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JTextField;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
import javax.swing.border.BevelBorder;
import javax.swing.border.Border;
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(textbox("Total"), gbc);
add(textbox("Tip %"), gbc);
add(textbox("People"), gbc);
}
public Component textbox(String x) {
JPanel panel = new JPanel(new GridBagLayout());
GridBagConstraints gbc = new GridBagConstraints();
gbc.gridwidth = GridBagConstraints.REMAINDER;
gbc.insets = new Insets(5, 5, 5, 5);
JLabel lbltAm = new JLabel(x);
JTextField tftAm = new JTextField(10);
lbltAm.setFont(new Font("Serif", Font.PLAIN, 14));
lbltAm.setForeground(Color.white);
panel.add(lbltAm, gbc);
panel.add(tftAm, gbc);
panel.setBackground(Color.DARK_GRAY);
Border lowerbevel = BorderFactory.createBevelBorder(BevelBorder.LOWERED);
panel.setBorder(lowerbevel);
return panel;
}
}
}
Or if your wanted the "boxes" to occupy the whole area when expanded, you could use something like...
GridBagConstraints gbc = new GridBagConstraints();
gbc.gridwidth = GridBagConstraints.REMAINDER;
gbc.weightx = 1;
gbc.weighty = 1;
gbc.fill = GridBagConstraints.BOTH;
add(textbox("Total"), gbc);
add(textbox("Tip %"), gbc);
add(textbox("People"), gbc);
Which results in something like...
Have a look at Laying Out Components Within a Container and How to Use GridBagLayout for more details
Firstly, I don't know why you're adding components to the JPanel with a method that is for BorderLayout after you've set the layout of panel as FlowLayout.
If you want to keep the components centered at all times (even if the screen is maximized), FlowLayout is probably not the best option for you to work with, as FlowLayout continues to position components horizontally until they fill the width of the frame (one row) and then, it begins the next row. Therefore, if the screen size is maximized, there will be more components per row and they won't be stacked. I would suggest using a GridLayout with one column like so:
frame.setLayout(new GridLayout(3 *(number of rows/components)*, 1));
You can do the same with the JPanel. After that, set the horizontal alignment of each component as centered like so:
component.setHorizontalAlignment(componentName.CENTER);
Hope this helps!
Try to use GridLayout instead of BoxLayout in createAndShowGUI.
frame.setLayout(new GridLayout(4,1));
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'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(...) );