I am trying to layout my JPanel such that:
The information label is at the top,
The text area is directly below, this and the info label go across (horizontally) the entire panel,
Below the text from left to right (equally spaced) are the config, save and then clear buttons,
Below those is the success/failure label (central, under the save button) and the home button (right, under the clear button).
I have tried many combinations and used other stack answers but cannot get it right, something about group layout I can't get my head around!
import javax.swing.*;
public class JFrameTest {
private static JFrame mainApp;
private static JPanel mainPanel;
public JFrameTest() {
mainApp = new JFrame("Application");
mainApp.setSize(640, 480);
mainApp.setLocationRelativeTo(null);
mainApp.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
mainApp.add(mainPanel());
mainApp.setVisible(true);
}
private JPanel mainPanel() {
JFrame.setDefaultLookAndFeelDecorated(true);
mainPanel= new JPanel();
mainPanel.setSize(600,450);
Container container = mainApp.getContentPane();
JLabel labelInfo = new JLabel("add necessary information here");
JLabel labelSOrF = new JLabel("Success/Failure");
// labelSOrF.setVisible(false);
JTextArea textArea = new JTextArea();
JButton configButton= new JButton("config");
JButton saveButton= new JButton("save");
JButton clearButton= new JButton("clear");
JButton homeButton= new JButton("Home");
GroupLayout layout = new GroupLayout(container);
container.setLayout(layout);
layout.setAutoCreateGaps(true);
layout.setAutoCreateContainerGaps(true);
layout.setHorizontalGroup(
layout.createSequentialGroup()
.addGroup(layout.createParallelGroup()
.addComponent(configButton)
.addGroup(layout.createParallelGroup()
.addComponent(saveButton)
.addComponent(labelSOrF, 0, GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
.addGroup(layout.createParallelGroup()
.addComponent(clearButton)
.addComponent(homeButton)
.addComponent(labelInfo)
.addComponent(textArea, 0, GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
)))
);
layout.setVerticalGroup(
layout.createSequentialGroup()
.addComponent(labelInfo)
.addComponent(textArea, 0, GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
.addGroup(layout.createParallelGroup()
.addGroup(layout.createSequentialGroup()
.addComponent(configButton)
.addComponent(saveButton)
.addComponent(buttonClear))
.addGroup(layout.createParallelGroup()
.addComponent(labelSOrF, 0, GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
.addComponent(homeButton)))
);
edit - added code
Oracle has a helpful tutorial, Creating a GUI With Swing. Skip the Learning Swing with the NetBeans IDE section.
GroupLayout was designed for GUI builders. No human being will deliberately use a GroupLayout. It's too hard to understand and as you've discovered, maintain.
Here's a GUI that meets your requirements.
Swing was designed to be constructed from the inside out. You layout the Swing components and let the JPanels and the JFrame size themselves. You don't start with the JFrame and fit all the Swing components.
All Swing applications must start with a call to the SwingUtilities invokeLater method. This method ensures that the Swing components are created and executed on the Event Dispatch Thread.
I created a main JPanel with three subordinate JPanels. To construct a complex GUI, you nest simple JPanels.
I added the Swing components to each JPanel in column, row order. That helps me to organize the code and makes it easier for readers of your code to understand the code.
Here's the complete runnable code.
import java.awt.BorderLayout;
import java.awt.FlowLayout;
import java.awt.GridLayout;
import javax.swing.BorderFactory;
import javax.swing.Box;
import javax.swing.BoxLayout;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTextArea;
import javax.swing.SwingUtilities;
public class JFrameTest implements Runnable {
public static void main(String[] args) {
SwingUtilities.invokeLater(new JFrameTest());
}
#Override
public void run() {
JFrame mainApp = new JFrame("Application");
mainApp.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
mainApp.add(createMainPanel(), BorderLayout.CENTER);
mainApp.pack();
mainApp.setLocationRelativeTo(null);
mainApp.setVisible(true);
}
private JPanel createMainPanel() {
JPanel panel = new JPanel();
panel.setLayout(new BoxLayout(panel, BoxLayout.PAGE_AXIS));
panel.add(createTitlePanel());
panel.add(createTextAreaPanel());
panel.add(createButtonPanel());
return panel;
}
private JPanel createTitlePanel() {
JPanel panel = new JPanel(new FlowLayout());
panel.setBorder(BorderFactory.createEmptyBorder(0, 5, 5, 5));
JLabel labelInfo = new JLabel("add necessary information here");
panel.add(labelInfo);
return panel;
}
private JPanel createTextAreaPanel() {
JPanel panel = new JPanel(new FlowLayout());
panel.setBorder(BorderFactory.createEmptyBorder(0, 5, 5, 5));
JTextArea textArea = new JTextArea(5, 40);
JScrollPane scrollPane = new JScrollPane(textArea);
scrollPane.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_ALWAYS);
panel.add(scrollPane);
return panel;
}
private JPanel createButtonPanel() {
JPanel panel = new JPanel(new GridLayout(0, 3, 20, 5));
panel.setBorder(BorderFactory.createEmptyBorder(0, 5, 5, 5));
JButton configButton = new JButton("config");
panel.add(configButton);
JButton saveButton = new JButton("save");
panel.add(saveButton);
JButton clearButton = new JButton("clear");
panel.add(clearButton);
JLabel dummy = new JLabel(" ");
panel.add(dummy);
JLabel labelSOrF = new JLabel("Success/Failure");
panel.add(labelSOrF);
JButton homeButton = new JButton("Home");
panel.add(homeButton);
return panel;
}
}
Related
JButton button1 = new JButton("Button 1");
JButton button2 = new JButton("Button 2");
JFrame frame = new JFrame();
frame.getContentPane().setLayout(new BorderLayout());
button2.setLayout(new FlowLayout(FlowLayout.RIGHT));
button1.setLayout(new FlowLayout(FlowLayout.LEFT));
frame.getContentPane().add(button1,BorderLayout.SOUTH);
frame.getContentPane().add(button2,BorderLayout.SOUTH);
frame.setSize(500,500);
frame.setVisible(true);
I'm trying to make Button 1 on the bottom left corner and Button 2 on the bottom right corner
__________________________
| |
| |
| |
| |
| |
| |
|Button1 Button2 |
|________________________|
You might want to consider using BoxLayout's horizontalGlue:
import java.awt.BorderLayout;
import javax.swing.Box;
import javax.swing.BoxLayout;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
public class ButtonsLeftAndRight {
private JFrame frame;
private JPanel pane;
private JButton button1;
private JButton button2;
public static void main(String[] args) {
SwingUtilities.invokeLater(new ButtonsLeftAndRight()::createAndShowGui);
}
public void createAndShowGui() {
frame = new JFrame(getClass().getSimpleName());
pane = new JPanel();
pane.setLayout(new BoxLayout(pane, BoxLayout.LINE_AXIS));
button1 = new JButton("Button1");
button2 = new JButton("Button2");
pane.add(button1);
pane.add(Box.createHorizontalGlue());
pane.add(button2);
frame.add(pane, BorderLayout.SOUTH);
frame.pack();
frame.setVisible(true);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
}
}
This might get you this, before and after resizing:
Create a container for both.
JPanel south = new JPanel(new AxisLayout(AxisLayout.HORIZONTAL));
south.add(button1);
south.add(button2);
frame.getContentPane().add(south, BorderLayout.SOUTH);
Obs: Sorry dont remember exactly Swing layout manangers, but you will find the AxisLayout to solve this
Another alternative is to use the GUI builder, and modify the code accordingly.
JButton button1 = new JButton("Button 1");
JButton button2 = new JButton("Button 2");
JFrame frame = new JFrame();
GroupLayout layout = new GroupLayout(frame.getContentPane());
frame.getContentPane().setLayout(layout);
layout.setHorizontalGroup(
layout.createParallelGroup(GroupLayout.Alignment.LEADING)
.addGroup(layout.createSequentialGroup()
.addGap(25, 25, 25)
.addComponent(button1)
.addPreferredGap(LayoutStyle.ComponentPlacement.RELATED, 215, Short.MAX_VALUE)
.addComponent(button2)
.addContainerGap())
);
layout.setVerticalGroup(
layout.createParallelGroup(GroupLayout.Alignment.LEADING)
.addGroup(javax.swing.GroupLayout.Alignment.TRAILING, layout.createSequentialGroup()
.addContainerGap(256, Short.MAX_VALUE)
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
.addComponent(button1)
.addComponent(button2))
.addGap(25, 25, 25))
);
frame.setSize(500, 500);
frame.setVisible(true);
You can add a jPanel and then add the two buttons to it, and then call setBounds on the buttons and specify the position. Then add the jPanel to the jFrame.
JButton button1 = new JButton("Button 1");
JButton button2 = new JButton("Button 2");
JFrame frame = new JFrame();
JPanel p = new JPanel();
p.setLayout(null);
button1.setBounds(10, 400, 100, 40);
p.add(button1);
button2.setBounds(375, 400, 100, 40);
p.add(button2);
frame.getContentPane().add(p);
frame.setSize(500, 500);
frame.setVisible(true);
The bounds are set as (x-coord, y-coord, width, height).
I am new to working with GUI's in Java and I am having a problem moving my text and buttons around. No matter what coordinates I give my button or any of the other JLabel it doesn't move, I was wondering how I could fix it this in such a way that I can place my components where ever I want on the JPanel
public class IntroPage extends JFrame {
public static void main(String[] args) {
IntroPage main = new IntroPage();
main.setVisible(true);
}
private JPanel contentPane;
public IntroPage (){
//make sure the program exits when the frame closes
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setTitle("Welcome");
contentPane = new JPanel();
setSize(400,700);
//This will center the JFrame in the middle of the screen
setLocationRelativeTo(null);
//Welcome Page stuff :D
JLabel ApplauseLabel = new JLabel("Welcome to U.X.Dot.X");
ApplauseLabel.setFont(new Font("Gill Sans MT", Font.PLAIN, 30));
ApplauseLabel.setLocation(100, 50);
contentPane.add(ApplauseLabel);
JLabel slogan = new JLabel("Register below");
slogan.setFont(new Font("Gill Sans MT", Font.PLAIN, 15));
slogan.setLocation(100, 400);
contentPane.add(slogan);
//FacebookSignUp.
JButton FBbutton = new JButton("Login With FaceBook");
FBbutton.setBackground(Color.BLUE);
FBbutton.setSize(50,50);
FBbutton.setLocation(20, 40);
FBbutton.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
//Add JPanel to go to FB API. Much later
}
});
contentPane.add(FBbutton);
add(contentPane);
//make sure the JFrame is visible
setVisible(true);
}
}
You're ignoring the layout managers of your contentPane JPanel. Understand that it uses FlowLayout by default, and will ignore your setLocation and setBounds statements. Ror the JPanel to accept absolute positioning, you would have to give it a null layout via contentPane.setLayout(null).
Having said that, I do not advise you to do this! While null layouts, setLocation(...) and setBounds(...) might seem to Swing newbies like the easiest and best way to create complex GUI's, the more Swing GUI'S you create the more serious difficulties you will run into when using them. They won't resize your components when the GUI resizes, they are a royal witch to enhance or maintain, they fail completely when placed in scrollpanes, they look gawd-awful when viewed on all platforms or screen resolutions that are different from the original one.
For example the following GUI
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.GridBagLayout;
import javax.swing.*;
public class IntroPage2 extends JPanel {
public static final String TITLE = "Welcome to U.X.Dot.X";
private JLabel welcomeLabel = new JLabel(TITLE, SwingConstants.CENTER);
private JButton fbButton = new JButton("Login With Facebook");
public IntroPage2() {
fbButton.setBackground(Color.BLUE);
fbButton.setForeground(Color.CYAN);
welcomeLabel.setFont(new Font(Font.SANS_SERIF, Font.PLAIN, 30));
int wlGap = 20;
welcomeLabel.setBorder(BorderFactory.createEmptyBorder(wlGap, wlGap, wlGap, wlGap));
JLabel registerBelowLabel = new JLabel("Register Below");
registerBelowLabel.setFont(new Font(Font.SANS_SERIF, Font.PLAIN, 15));
JPanel centralPanel = new JPanel(new GridBagLayout());
centralPanel.setPreferredSize(new Dimension(300, 600));
centralPanel.add(registerBelowLabel);
JPanel topPanel = new JPanel(new BorderLayout());
topPanel.add(fbButton, BorderLayout.LINE_START);
topPanel.add(welcomeLabel, BorderLayout.CENTER);
setLayout(new BorderLayout());
int ebGap = 8;
setBorder(BorderFactory.createEmptyBorder(ebGap, ebGap, ebGap, ebGap));
add(topPanel, BorderLayout.PAGE_START);
add(centralPanel, BorderLayout.CENTER);
}
private static void createAndShowGui() {
IntroPage2 mainPanel = new IntroPage2();
JFrame frame = new JFrame("Welcome");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.getContentPane().add(mainPanel);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(() -> createAndShowGui());
}
}
would create something like:
Like the title said, the hearder name is just not showing up. i tried many options like using a JScrollPane. and fallowed many guide on this forum but no help. I really wanted to get resolve problem by myself but i have tried everything and out of option.
import java.awt.Color;
import java.awt.EventQueue;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JLabel;
import java.awt.Font;
import java.awt.ScrollPane;
import javax.swing.JButton;
import java.awt.event.ActionListener;
import java.sql.Connection;
import java.sql.DatabaseMetaData;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.Statement;
import java.awt.event.ActionEvent;
import javax.swing.JTable;
import javax.swing.JTextField;
import javax.swing.table.DefaultTableModel;
import javax.swing.table.TableColumn;
import javax.swing.JScrollPane;
import javax.swing.JScrollBar;
public class Adminpage extends JPanel {
private JFrame frame;
static String ID[]={"name","Username","Password"};
static DefaultTableModel model;
private JTextField NametextField;
private JTextField UsertextField;
private JTextField PasstextField;
private JTable table;
private JScrollPane scroll;
/**
* Create the panel.
*/
private void initialize() {
frame = new JFrame();
frame.setBounds(100, 100, 497, 545);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.getContentPane().setLayout(null);
JLabel lblAdminstratorPortal = new JLabel("Adminstrator Portal");
lblAdminstratorPortal.setFont(new Font("Tahoma", Font.BOLD, 20));
lblAdminstratorPortal.setBounds(109, 26, 218, 25);
frame.getContentPane().add(lblAdminstratorPortal);
JButton btnNewButton = new JButton("Add Librarian");
btnNewButton.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent arg0) {
//model= (DefaultTableModel)table.getModel();
//model.addColumn("name");
//model.addColumn("Username");
//model.addColumn("Password");
model.addRow(new Object[]{NametextField.getText(),UsertextField.getText(),PasstextField.getText()});
}
});
btnNewButton.setBounds(10, 62, 108, 35);
frame.getContentPane().add(btnNewButton);
JButton btnDelete = new JButton("Delete");
btnDelete.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
}
});
btnDelete.setBounds(130, 62, 108, 35);
frame.getContentPane().add(btnDelete);
JButton btnViewLibrarian = new JButton("View Librarian");
btnViewLibrarian.setBounds(245, 62, 108, 35);
frame.getContentPane().add(btnViewLibrarian);
JButton btnLogout = new JButton("Logout");
btnLogout.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent arg0) {
frame.dispose();
}
});
btnLogout.setBounds(363, 62, 108, 35);
frame.getContentPane().add(btnLogout);
//model= (DefaultTableModel)table.getModel();
JLabel lblName = new JLabel("Name");
lblName.setBounds(21, 144, 60, 14);
frame.getContentPane().add(lblName);
JLabel lblUsername = new JLabel("Username");
lblUsername.setBounds(21, 195, 60, 14);
frame.getContentPane().add(lblUsername);
JLabel lblPassword = new JLabel("Password");
lblPassword.setBounds(21, 250, 60, 14);
frame.getContentPane().add(lblPassword);
NametextField = new JTextField();
NametextField.setBounds(119, 141, 119, 20);
frame.getContentPane().add(NametextField);
NametextField.setColumns(10);
UsertextField = new JTextField();
UsertextField.setColumns(10);
UsertextField.setBounds(119, 192, 119, 20);
frame.getContentPane().add(UsertextField);
PasstextField = new JTextField();
PasstextField.setColumns(10);
PasstextField.setBounds(119, 247, 119, 20);
frame.getContentPane().add(PasstextField);
table = new JTable();
table.setBounds(10, 304, 461, 189);
frame.getContentPane().add(table);
EventQueue.invokeLater(new Runnable() {
public void run() {
try {
frame.setVisible(true);
} catch (Exception e) {
e.printStackTrace();
}
}
});
}
public Adminpage() {
database();
setLayout(null);
initialize();
model= (DefaultTableModel)table.getModel();
model.addColumn("name");
model.addColumn("Username");
model.addColumn("Password");
}
public void database(){
try {
Class.forName("sun.jdbc.odbc.JdbsOdbcDriver");
Connection con = DriverManager.getConnection("jdbc:odbc:Games");
Statement st = con.createStatement();
String getquery = ("Select* from Games");
ResultSet rs= st.executeQuery(getquery);
while(rs.next()){
System.out.println(rs.getString(2));
}
} catch (Exception e) {
System.out.println(e.getMessage());
}
}
}
You're adding the JTable directly to the GUI. Instead, yes you need to embed the JTable into the viewport of a JScrollPane and then add the JScrollPane to the GUI.
For example:
table = new JTable();
JScrollPane scrollPane = new JScrollPane(table);
// table.setBounds(10, 304, 461, 189);
scrollPane.setBounds(10, 304, 461, 189); // This is bad, but will leave for now
// frame.getContentPane().add(table);
frame.getContentPane().add(scrollPane);
Also, you're harming your GUI by using null layouts and absolute positioning, as this can interfere with a component's ability to show itself fully and correctly, to achieve its own preferred size. Much better is to learn and use the layout managers.
For instance, when I run your program on my platform, I see:
Note how the buttons do not show their full texts due to their not being allowed to achieve their preferred sizes.
For example, using BoxLayout with some nested JPanels, one using GridLayout(1, 0, 5, 0) for one row, variable number of columns, and a 5 point horizontal gap between components, and another nested JPanel using GridBagLayout, for placement of JTextFields and JLabels, and some "wrapper" JPanels using default FlowLayout to center components within them...
import java.awt.Dimension;
import java.awt.Font;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.GridLayout;
import java.awt.Insets;
import javax.swing.*;
import javax.swing.table.DefaultTableModel;
public class AdminPage2 extends JPanel {
private static final long serialVersionUID = 1L;
public static final String TITLE = "Administrator Portal";
private static final Font TITLE_FONT = new Font("Tahoma", Font.BOLD, 20);
private static final String[] COL_NAMES = {"Name", "User Name", "Password"};
private int txtFieldCols = 20;
private JTextField nameField = new JTextField(txtFieldCols);
private JTextField userNameField = new JTextField(txtFieldCols);
private JPasswordField passwordField = new JPasswordField(txtFieldCols);
private DefaultTableModel tableModel = new DefaultTableModel(COL_NAMES, 0);
private JTable table = new JTable(tableModel);
private JScrollPane tableScrollPane = new JScrollPane(table);
public AdminPage2() {
JLabel titleLabel = new JLabel(TITLE, SwingConstants.CENTER);
titleLabel.setFont(TITLE_FONT);
JPanel titlePanel = new JPanel();
titlePanel.add(titleLabel);
JPanel buttonPanel = new JPanel(new GridLayout(1, 0, 5, 0));
// of course you'd add ActionListeners or Actions to your buttons
buttonPanel.add(new JButton("Add Library"));
buttonPanel.add(new JButton("Delete"));
buttonPanel.add(new JButton("View Library"));
buttonPanel.add(new JButton("Logout"));
JPanel textFieldPanel = new JPanel(new GridBagLayout());
textFieldPanel.add(new JLabel("Name:"), createGbc(0, 0));
textFieldPanel.add(nameField, createGbc(1, 0));
textFieldPanel.add(new JLabel("User Name:"), createGbc(0, 1));
textFieldPanel.add(userNameField, createGbc(1, 1));
textFieldPanel.add(new JLabel("Password:"), createGbc(0, 2));
textFieldPanel.add(passwordField, createGbc(1, 2));
JPanel wrapTfPanel = new JPanel();
wrapTfPanel.add(textFieldPanel);
Dimension scrollPanePrefSz = tableScrollPane.getPreferredSize();
int w = scrollPanePrefSz.width;
int h = scrollPanePrefSz.height / 2;
scrollPanePrefSz = new Dimension(w, h);
tableScrollPane.setPreferredSize(scrollPanePrefSz);
// put together main JPanel components
int ebGap = 4;
setBorder(BorderFactory.createEmptyBorder(ebGap, ebGap, ebGap, ebGap));
setLayout(new BoxLayout(this, BoxLayout.PAGE_AXIS));
add(Box.createVerticalStrut(5));
add(titlePanel);
add(Box.createVerticalStrut(5));
add(buttonPanel);
add(Box.createVerticalStrut(5));
add(wrapTfPanel);
add(Box.createVerticalStrut(5));
add(tableScrollPane);
}
// create constraints to use when adding component to GridBagLayout
private GridBagConstraints createGbc(int x, int y) {
GridBagConstraints gbc = new GridBagConstraints();
gbc.gridx = x;
gbc.gridy = y;
gbc.gridwidth = 1;
gbc.gridheight = 1;
gbc.weightx = 1.0;
gbc.weighty = 1.0;
gbc.anchor = x == 0 ? GridBagConstraints.WEST : GridBagConstraints.EAST;
gbc.fill = GridBagConstraints.HORIZONTAL;
int in = 10;
int leftIn = x == 0 ? 4 * in : in;
gbc.insets = new Insets(in, leftIn, in, in);
return gbc;
}
public static void main(String[] args) {
SwingUtilities.invokeLater(() -> createAndShowGui());
}
private static void createAndShowGui() {
AdminPage2 mainPanel = new AdminPage2();
JFrame frame = new JFrame("Administrator Page");
frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
frame.add(mainPanel);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
}
Which displays as:
Regarding your questions:
but new question why give the scrollpane a bound instead of the table itself
The JScrollPane holds the JTable within it, and so if you use null layouts (which you shouldn't), and you're adding this JTable-containing JScrollPane to the GUI, you must set its bounds. Much better though is to use layout managers as I've outlined above. It makes it much easier to modify the GUI later and to debug it now.
and why adding the scrollpane into the panel instead of the table.
Because that's how JScrollPanes work. They don't add scrollbars to a component but rather nest the component itself, here the JTable, within the JScrollPane's viewport. Please read the JScrollPane Tutorial (see link) to see the details on this.
A further note on the power of layout managers. Say you want to add a new JLabel / JTextField combination, one that accepts a password hint, and say the JTextField's name is passwordHint. If you were using null layouts and absolute positioning, you'd have to set the bounds of your new JLabel and JTextField, but you'd also have to change the bounds of all components below and to the right of it, and would have to re-set the size of the GUI manually. If your GUI is very complex, this can lead to bugs and a lot of frustration.
If you used the layout managers above however, all you'd need to do would be to add two lines of code to the textFieldPanel JPanel creational code as shown below with the obvious comments:
// original textFieldPanel creational code
JPanel textFieldPanel = new JPanel(new GridBagLayout());
textFieldPanel.add(new JLabel("Name:"), createGbc(0, 0));
textFieldPanel.add(nameField, createGbc(1, 0));
textFieldPanel.add(new JLabel("User Name:"), createGbc(0, 1));
textFieldPanel.add(userNameField, createGbc(1, 1));
textFieldPanel.add(new JLabel("Password:"), createGbc(0, 2));
textFieldPanel.add(passwordField, createGbc(1, 2));
// !! ****** these lines added ******
textFieldPanel.add(new JLabel("Password Hint:"), createGbc(0, 3));
textFieldPanel.add(passwordHint, createGbc(1, 3));
This results in a perfect placement of the new components without adversely affecting the old:
Is setting preferred size of JFrame using setPreferredSize() considered bad?
If it is bad, what is good way to change JFrame window size to dimension I need.
I know laying components in way to reflect final JFrame dimension that I need. But if I use shortcut to change preferred size using call setPreferredSize() just before call to pack() to change final JFrame size is bad? If so why?
For example I have sample form:
This is displayed without setting preferred size.
Now I can resize form with call to setPreferredSize() before call to pack().
This is displayed with call: setPreferredSize(new Dimension(500, 300));
I can have similar effect with setting components size while laying it out. But what is disadvantage of setting frame size with just call to setPreferredSize().
I can think setting preferred size as resizing displayed window manually with mouse after it has been displayed. Isn't it?
Code:
import java.awt.Dimension;
import javax.swing.GroupLayout;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JScrollPane;
import javax.swing.JTextArea;
import javax.swing.JTextField;
import javax.swing.SwingUtilities;
public class MyFrame extends JFrame {
private static final long serialVersionUID = 1L;
private JTextField fullNameTextField = new JTextField();
private JTextField emailIDTextField = new JTextField();
private JTextArea addressTextArea = new JTextArea();
private JButton submitButton = new JButton("Submit");
private JButton cancelButton = new JButton("Cancel");
public MyFrame(){
super("MyFrame");
layoutComponents();
setDefaultCloseOperation(EXIT_ON_CLOSE);
setLocationByPlatform(true);
setPreferredSize(new Dimension(500, 300));
pack();
}
private void layoutComponents(){
GroupLayout layout = new GroupLayout(getContentPane());
getContentPane().setLayout(layout);
JLabel fullNameLabel = new JLabel("Full Name:");
JLabel emailIDLabel = new JLabel("Email ID:");
JLabel addressLabel = new JLabel("Address:");
JScrollPane addressScrollPane = new JScrollPane(addressTextArea);
layout.setHorizontalGroup(
layout.createParallelGroup(GroupLayout.Alignment.LEADING)
.addGroup(layout.createSequentialGroup()
.addGap(10)
.addGroup(layout.createParallelGroup(GroupLayout.Alignment.LEADING)
.addGroup(layout.createSequentialGroup()
.addGroup(layout.createParallelGroup(GroupLayout.Alignment.LEADING)
.addComponent(fullNameLabel)
.addComponent(emailIDLabel)
.addComponent(addressLabel)
)
.addGap(15)
.addGroup(layout.createParallelGroup(GroupLayout.Alignment.LEADING)
.addComponent(fullNameTextField, 10, 200, Short.MAX_VALUE)
.addComponent(emailIDTextField)
.addComponent(addressScrollPane)
.addGroup(layout.createSequentialGroup()
.addGap(0, 0, Short.MAX_VALUE)
.addComponent(submitButton)
.addGap(10)
.addComponent(cancelButton)
)
)
)
)
.addGap(10)
)
);
layout.setVerticalGroup(
layout.createParallelGroup(GroupLayout.Alignment.LEADING)
.addGroup(layout.createSequentialGroup()
.addGap(10)
.addGroup(layout.createParallelGroup(GroupLayout.Alignment.BASELINE)
.addComponent(fullNameLabel)
.addComponent(fullNameTextField)
)
.addGap(5)
.addGroup(layout.createParallelGroup(GroupLayout.Alignment.BASELINE)
.addComponent(emailIDLabel)
.addComponent(emailIDTextField)
)
.addGap(5)
.addGroup(layout.createParallelGroup(GroupLayout.Alignment.BASELINE)
.addComponent(addressLabel)
.addComponent(addressScrollPane, 20, 60, Short.MAX_VALUE)
)
.addGap(15)
.addGroup(layout.createParallelGroup(GroupLayout.Alignment.BASELINE)
.addComponent(submitButton)
.addComponent(cancelButton)
)
.addGap(10)
)
);
layout.linkSize(submitButton, cancelButton);
getRootPane().setDefaultButton(submitButton);
}
public static void main(String [] args){
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
new MyFrame().setVisible(true);
}
});
}
}
The main reason it would be considered bad (bad is probably too strong a word, unwise might be better) is the use of unknown (magic) numbers, rather than empirical values.
Every platform (and even similar OS running on different hardware and settings) has it's own means for rendering content which can change the amount of space that individual components require.
In regards to things like text fields and text-areas, you can make suggestions about the number of columns (and rows for text areas) which can be used to change a frames final size, using setColumns and setRows for example...
So, using the following code...
import java.awt.EventQueue;
import java.awt.FlowLayout;
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.JScrollPane;
import javax.swing.JTextArea;
import javax.swing.JTextField;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
public class TestLayout101 {
public static void main(String[] args) {
new TestLayout101();
}
public TestLayout101() {
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 {
private JTextField fullNameTextField = new JTextField(10);
private JTextField emailIDTextField = new JTextField(10);
private JTextArea addressTextArea = new JTextArea(10, 20);
private JButton submitButton = new JButton("Submit");
private JButton cancelButton = new JButton("Cancel");
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("Full Name: "), gbc);
gbc.gridy++;
add(new JLabel("Email ID: "), gbc);
gbc.gridy++;
gbc.anchor = GridBagConstraints.NORTHWEST;
add(new JLabel("Address: "), gbc);
gbc.gridx++;
gbc.gridy = 0;
gbc.fill = GridBagConstraints.HORIZONTAL;
gbc.weightx = 1;
add(fullNameTextField, gbc);
gbc.gridy++;
add(emailIDTextField, gbc);
gbc.gridy++;
gbc.weighty = 1;
add(new JScrollPane(addressTextArea), gbc);
JPanel buttons = new JPanel(new FlowLayout(FlowLayout.RIGHT, 4, 4));
buttons.add(submitButton);
buttons.add(cancelButton);
gbc.gridx = 0;
gbc.gridy++;
gbc.gridwidth = GridBagConstraints.REMAINDER;
gbc.weighty = 0;
add(buttons, gbc);
}
}
}
Which produces...
Now by changing only one line...
private JTextArea addressTextArea = new JTextArea(10, 20);
// Only this value ---^
It produces this...
And again...
private JTextArea addressTextArea = new JTextArea(10, 40);
// Only this value ---^
I could change the number of rows for the JTextArea and effect the height of the window as well.
The difference is, these values are used in combination with the systems font metrics to calculate the preferred size of the components when the program is run, so it will be different for different systems and platforms...
The main point to layouts is to spend your time focused on the intent and the work flow and not trying to get a pixel perfect solution, because there's simply no such thing...talk to web developers, they have the same issues, just much worse (multiple browsers on a single system, all rendering differently)
Personally, I think there is nothing wrong with using frame.setPreferredSize() to resize a JFrame window. In fact, I think it is the most appropriate solution for resizing it.
But this is a good time to make a distinction between what some of the most common resizing methods do:
frame.pack(), as explained here basically takes the prefered sizes for each of the individual elements.
frame.setSize() resizes the window as expected, but only if a component's parent has no layout manager expressly defined.
There is a discussion concerning the use of this method here.
That doesn't really leave any other methods to use to define the size of a window, which is why I think setting the preferred window size is the best way go about it. I would still make sure to define setMinimumSize() and setMaximumSize() to ensure the components are resized correctly in all cases.
Edit:
Other posts in this thread got me thinking about the use of 'magic constants' in setting a components. In web development, for instance, it is generally frowned upon to use pixels, for example, as this changes widely by system. I wanted to see if this was also true with dimensions. In short, my findings concluded it doesn't.
This was a JFrame with a prefered size of new Dimension(200, 300) taken on my MacBook Pro with Retina Display:
This was a JFrame with a prefered size of new Dimension(200, 300) taken on my Asus Windows Tablet (non-retina display):
These windows looked the exact same and, although I couldn't find this published, the Dimenion is really an already-scaled proportion, taking into consideration the height, width, and resolution of the display to produce a near accurate rendering across all devices. This, I guess you could say, makes sense because it is the Java way.
That being said, here's some things I think need to watched out for:
The difference between UIManager LookAndFeel across platforms as MadProgrammer pointed out
Honestly, I think the final decision is left up to the programmer. After this testing, I pretty much still conclude that using the frame.setPreferredSize() is one of the best things available if you don't intend to a implement a semi-complex window manager. Even then, I think there will always be some unaccounted variation.
m trying to add a Jslider and a Jlabel to PAGE_END beside each other, i can add each of them on their on but adding .add("components name".PAGE_END) to both it on lets one exist there?
so basicly i want to create my slider and a jlabel beside it to the right, could someone please help in anyway, thanks.
package assignment;
//import java.awt.FlowLayout;
import java.awt.BorderLayout;
import javax.swing.JLabel;
import javax.swing.JSlider;
import javax.swing.JTextField;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;
public class MyControlPanel extends javax.swing.JPanel {
JSlider slider;
JLabel sliderLabel;
JLabel blank;
public MyControlPanel() {
slider = new JSlider();
slider.setValue(50);
slider.addChangeListener(new MyChangeAction());
slider.setMajorTickSpacing(10);
slider.setPaintLabels(true);
slider.setPaintTicks(true);
slider.setBounds(300, 50, 100, 50);
sliderLabel = new JLabel("50");
blank = new JLabel(" ");
JTextField boundary_length = new JTextField("Boundary Length");
JTextField area = new JTextField("Area");
setLayout(new BorderLayout());
this.add(slider, BorderLayout.PAGE_END);
this.add(sliderLabel, BorderLayout.LINE_END);
this.add(area);
this.add(boundary_length);
this.add(blank, BorderLayout.LINE_START);
}
#SuppressWarnings("unchecked")
// <editor-fold defaultstate="collapsed" desc="Generated Code">
private void initComponents() {
javax.swing.GroupLayout layout = new javax.swing.GroupLayout(this);
this.setLayout(layout);
layout.setHorizontalGroup(
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGap(0, 400, Short.MAX_VALUE)
);
layout.setVerticalGroup(
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGap(0, 300, Short.MAX_VALUE)
);
}// </editor-fold>
// Variables declaration - do not modify
// End of variables declaration
public class MyChangeAction implements ChangeListener{
//complete code here
public void stateChanged(ChangeEvent ce)
{
int value = slider.getValue();
String str = Integer.toString(value);
sliderLabel.setText(str);
}
} // end class
Instead of using BorderLayout to place both the slider and the label, create a panel containing both components, then use BorderLayout to place the panel.
Replace
this.add(slider, BorderLayout.PAGE_END);
this.add(sliderLabel, BorderLayout.LINE_END);
With
JPanel sliderPanel = new JPanel();
sliderPanel.setLayout( new FlowLayout(FlowLayout.TRAILING));
sliderPanel.add(slider);
sliderPanel.add(sliderLabel);
this.add(sliderPanel, BorderLayout.PAGE_END);
This is based on code I used to place an OK and Cancel button at the bottom of a dialog. This might not compile - but you get the idea. Play around with the arguments to FlowLayout and change the order of adding to the slider panel until you get the look you want.
BTW - since you are doing the layout yourself - not using a GUI builder - you might as well get rid of the initComponents method and the surrounding comments. I'm guessing that you are using Netbeans and created a panel that was initially configured to use GroupLayout, and Netbeans injected that code. Now it's just getting in the way.