JPanel component placement - java

I am trying to get a basic GUI program. It doesnt have to do much but the buttons at the bottom have to work. I am having trouble placing the components (The text, combobox, etc) in the proper spot. Using the GridBag I am able to change the location with the c.gridx and c.gridy but in a very weird way. If I put the gridy values 0-7 with x being 0 everything is on top of eachother. I try putting the combo box next to the text by changing the gridx value and everything gets messed up. The alignment is off on the components after the one I was trying to move over. How do I fix this? I tried the BorderLayout.DIRECTION with no luck. The actual change doesn't take effect at all (moving then to the bottom). How do I fix this? Thanks
/*
* To change this license header, choose License Headers in Project Properties.
* To change this template file, choose Tools | Templates
* and open the template in the editor.
*/
package javaredesign;
import java.awt.BorderLayout;
import java.awt.FlowLayout;
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.JComboBox;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JTextField;
import javax.swing.Timer;
/**
*
* #author
*/
public class Window extends JFrame {
//Default global variables
private JButton submit;
private JButton cancel;
private JButton reset;
private JPanel panel = new JPanel(new GridBagLayout());
private String searchOutput = "";
//Default constructor
public Window() {
//Title of the window
super("LIBSYS: Search");
//Creating the flow layout to which we can work on and adding panel to frame
setLayout(new FlowLayout());
add(panel, BorderLayout.SOUTH);
//Creating the grid to location of the parts
GridBagConstraints c = new GridBagConstraints();
//Initializing the buttons
submit = new JButton("Submit");
c.insets = new Insets(10, 10, 10, 5);
c.gridx = 1;
c.gridy = 20;
panel.add(submit, c);
reset = new JButton("Reset");
c.gridx = 2;
c.gridy = 20;
panel.add(reset, c);
cancel = new JButton("Cancel");
c.gridx = 3;
c.gridy = 20;
panel.add(cancel, c);
//Handler constructor to do the actionlistening
Handler handler = new Handler();
submit.addActionListener(handler);
reset.addActionListener(handler);
cancel.addActionListener(handler);
//Creating the two dropdowns with the words next to them
JLabel chooseCollection = new JLabel("Choose Collection");
String[] ccString = {"All", "Mostly", "Some", "Few"};
JComboBox ccComboBox = new JComboBox(ccString);
JLabel searchUsing = new JLabel("Search Using");
String[] suString = {"Title", "Artist", "Arthor", "Type"};
JComboBox suComboBox = new JComboBox(suString);
//Adding all the text and dropdown menus to the panel
c.gridx = 0;
c.gridy = 24;
panel.add(chooseCollection, c);
c.gridx = 0;
c.gridy = 25;
panel.add(ccComboBox, c);
c.gridx = 1;
c.gridy = 25;
panel.add(searchUsing, c);
c.gridx = 0;
c.gridy = 27;
panel.add(suComboBox, c);
c.gridx = 1;
c.gridy = 27;
//Setting up the text inputbox
JLabel keyword = new JLabel("Keyword or phrase");
JTextField textField = new JTextField(20);
//Adding lable and text box to the panel
panel.add(keyword);
panel.add(textField);
}
}

I would not try and force the buttons into the same layout as the other components. They are independant and should be free floating evenly spaced.
The button panel should be in a panel set with flowlayout. Then this panel should be added to the frame with borderlayout at SOUTH.
The rest of the components should go into a panel with gridbaglayout and added to the frame at CENTER.
The gridbaglayout panel then should have all its components set at the coordinates listed in the picture. If the component covers more than two cells (ie combo) then set the gridWidth property to 2.

I'd do it by having an outer BorderLayout. In the CENTER would be a GroupLayout for the label/control pairs. In the PAGE_END would be a FlowLayout for the buttons.
This uses a TitledBorder instead of the JLabel to show LIBSYS Search. If it really needs a label, put it in the PAGE_START of the border layout.
import java.awt.*;
import java.util.HashMap;
import java.util.Map;
import javax.swing.*;
import javax.swing.border.*;
public class LibSysSearch {
private JComponent ui = null;
LibSysSearch() {
initUI();
}
public void initUI() {
if (ui != null) {
return;
}
ui = new JPanel(new BorderLayout(4, 4));
ui.setBorder(new EmptyBorder(4, 4, 4, 4));
// Here is our control. This puts a titled border around it,
// instead of using a label in the PAGE_START
JPanel libSysSearchControl = new JPanel(new BorderLayout());
ui.add(libSysSearchControl);
JPanel actionPanel = new JPanel(
new FlowLayout(FlowLayout.CENTER, 15, 15));
libSysSearchControl.add(actionPanel, BorderLayout.PAGE_END);
String[] actionNames = {"Search", "Reset", "Cancel"};
for (String name : actionNames) {
actionPanel.add(new JButton(name));
}
// Use GroupLayout for the label/cotrnl combos.
// make the arrays for the factory method
String[] labels = {
"Choose Collection", "Search Using",
"Keyword or phrase", "Adjacent words"
};
String[] ccString = {"All", "Mostly", "Some", "Few"};
String[] suString = {"Title", "Artist", "Arthor", "Type"};
JPanel confirmAdjacent = new JPanel(new FlowLayout(FlowLayout.LEADING,5,0));
confirmAdjacent.add(new JRadioButton("Yes", true));
confirmAdjacent.add(new JRadioButton("No"));
JComponent[] controls = {
new JComboBox(ccString),
new JTextField(20),
new JComboBox(suString),
confirmAdjacent
};
libSysSearchControl.add(getTwoColumnLayout(labels, controls));
// throw in a few borders for white space
Border border = new CompoundBorder(
new EmptyBorder(10, 10, 10, 10),
new TitledBorder("LIBSYS Search"));
border = new CompoundBorder(
border,
new EmptyBorder(10, 10, 10, 10));
libSysSearchControl.setBorder(border);
}
public JComponent getUI() {
return ui;
}
public static void main(String[] args) {
Runnable r = new Runnable() {
#Override
public void run() {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (Exception useDefault) {
}
LibSysSearch o = new LibSysSearch();
JFrame f = new JFrame("Library System Search");
f.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
f.setLocationByPlatform(true);
f.setContentPane(o.getUI());
f.pack();
f.setMinimumSize(f.getSize());
f.setVisible(true);
}
};
SwingUtilities.invokeLater(r);
}
/**
* Provides a JPanel with two columns (labels & fields) laid out using
* GroupLayout. The arrays must be of equal size.
*
* Typical fields would be single line textual/input components such as
* JTextField, JPasswordField, JFormattedTextField, JSpinner, JComboBox,
* JCheckBox.. & the multi-line components wrapped in a JScrollPane -
* JTextArea or (at a stretch) JList or JTable.
*
* #param labels The first column contains labels.
* #param fields The last column contains fields.
* #param addMnemonics Add mnemonic by next available letter in label text.
* #return JComponent A JPanel with two columns of the components provided.
*/
public static JComponent getTwoColumnLayout(
JLabel[] labels,
JComponent[] fields,
boolean addMnemonics) {
if (labels.length != fields.length) {
String s = labels.length + " labels supplied for "
+ fields.length + " fields!";
throw new IllegalArgumentException(s);
}
JComponent panel = new JPanel();
GroupLayout layout = new GroupLayout(panel);
panel.setLayout(layout);
// Turn on automatically adding gaps between components
layout.setAutoCreateGaps(true);
// Create a sequential group for the horizontal axis.
GroupLayout.SequentialGroup hGroup = layout.createSequentialGroup();
GroupLayout.Group yLabelGroup = layout.createParallelGroup(GroupLayout.Alignment.TRAILING);
hGroup.addGroup(yLabelGroup);
GroupLayout.Group yFieldGroup = layout.createParallelGroup();
hGroup.addGroup(yFieldGroup);
layout.setHorizontalGroup(hGroup);
// Create a sequential group for the vertical axis.
GroupLayout.SequentialGroup vGroup = layout.createSequentialGroup();
layout.setVerticalGroup(vGroup);
int p = GroupLayout.PREFERRED_SIZE;
// add the components to the groups
for (JLabel label : labels) {
yLabelGroup.addComponent(label);
}
for (Component field : fields) {
yFieldGroup.addComponent(field, p, p, p);
}
for (int ii = 0; ii < labels.length; ii++) {
vGroup.addGroup(layout.createParallelGroup().
addComponent(labels[ii]).
addComponent(fields[ii], p, p, p));
}
if (addMnemonics) {
addMnemonics(labels, fields);
}
return panel;
}
private final static void addMnemonics(
JLabel[] labels,
JComponent[] fields) {
Map<Character, Object> m = new HashMap<Character, Object>();
for (int ii = 0; ii < labels.length; ii++) {
labels[ii].setLabelFor(fields[ii]);
String lwr = labels[ii].getText().toLowerCase();
for (int jj = 0; jj < lwr.length(); jj++) {
char ch = lwr.charAt(jj);
if (m.get(ch) == null && Character.isLetterOrDigit(ch)) {
m.put(ch, ch);
labels[ii].setDisplayedMnemonic(ch);
break;
}
}
}
}
/**
* Provides a JPanel with two columns (labels & fields) laid out using
* GroupLayout. The arrays must be of equal size.
*
* #param labelStrings Strings that will be used for labels.
* #param fields The corresponding fields.
* #return JComponent A JPanel with two columns of the components provided.
*/
public static JComponent getTwoColumnLayout(
String[] labelStrings,
JComponent[] fields) {
JLabel[] labels = new JLabel[labelStrings.length];
for (int ii = 0; ii < labels.length; ii++) {
labels[ii] = new JLabel(labelStrings[ii]);
}
return getTwoColumnLayout(labels, fields);
}
/**
* Provides a JPanel with two columns (labels & fields) laid out using
* GroupLayout. The arrays must be of equal size.
*
* #param labels The first column contains labels.
* #param fields The last column contains fields.
* #return JComponent A JPanel with two columns of the components provided.
*/
public static JComponent getTwoColumnLayout(
JLabel[] labels,
JComponent[] fields) {
return getTwoColumnLayout(labels, fields, true);
}
}

The problem is that you need to understand GridBagLayout. You need to layout your components onto a proper grid:
So you should have 5 rows and 12 columns to layout the way you described in your picture. Don't mind the padding (I tried to add that to make it more illustrative). Your first three rows should each have elements of gridwidth = 6 and the first should be at gridx = 0 and the second at gridx = 6. Then your "yes" and "no" buttons should each have gridwidth = 3 at gridx = 6 and gridx = 9, respectively. Finally your last row, the buttons, should each have gridwidth = 2 and gridx = 1, gridx = 5, and gridx = 9, respectively. Instead of using padding, I would just use gbc.anchor = GridBagConstraints.CENTER (for all of the components) so that the components are centered inside of their grid.
I would suggest using gbc.fill = GridBagConstraints.HORIZONTAL for the text field and combo boxes so that they line up "nice and pretty" but don't forget to set that back to gbc.fill = GridBagConstraints.NONE for the radio buttons and regular buttons (unless you want them to stretch as well).
I very haphazardly changed your code (which appeared to be in no logical order):
// Creating the grid to location of the parts
GridBagConstraints c = new GridBagConstraints();
c.anchor = GridBagConstraints.CENTER;
// Initializing the buttons
submit = new JButton("Submit");
c.insets = new Insets(10, 10, 10, 5);
c.gridx = 1;
c.gridy = 5;
c.gridwidth = 2;
panel.add(submit, c);
reset = new JButton("Reset");
c.gridx = 5;
// c.gridy = 20;
panel.add(reset, c);
cancel = new JButton("Cancel");
c.gridx = 9;
// c.gridy = 20;
panel.add(cancel, c);
// Handler constructor to do the actionlistening
Handler handler = new Handler();
submit.addActionListener(handler);
reset.addActionListener(handler);
cancel.addActionListener(handler);
// Creating the two dropdowns with the words next to them
JLabel chooseCollection = new JLabel("Choose Collection");
String[] ccString = { "All", "Mostly", "Some", "Few" };
JComboBox ccComboBox = new JComboBox(ccString);
JLabel searchUsing = new JLabel("Search Using");
String[] suString = { "Title", "Artist", "Arthor", "Type" };
JComboBox suComboBox = new JComboBox(suString);
// Adding all the text and dropdown menus to the panel
c.gridx = 0;
c.gridy = 0;
c.gridwidth = 6;
c.anchor = GridBagConstraints.WEST;
panel.add(chooseCollection, c);
c.gridx = 6;
// c.gridy = 25;
c.fill = GridBagConstraints.HORIZONTAL;
panel.add(ccComboBox, c);
c.gridx = 0;
c.gridy = 2;
c.fill = GridBagConstraints.NONE;
panel.add(searchUsing, c);
c.gridx = 6;
// c.gridy = 27;
c.fill = GridBagConstraints.HORIZONTAL;
panel.add(suComboBox, c);
// Setting up the text inputbox
JLabel keyword = new JLabel("Keyword or phrase");
JTextField textField = new JTextField(20);
// Adding lable and text box to the panel
c.gridx = 0;
c.gridy = 1;
c.fill = GridBagConstraints.NONE;
panel.add(keyword, c);
c.gridx = 6;
panel.add(textField, c);
Which produced the following layout:

i would probably do it like this:
public class Test {
public Test() {
JFrame frame = new JFrame();
JPanel mainPanel = new JPanel(new GridLayout(0, 1));
JPanel chooseCollectionPanel = new JPanel(new BorderLayout());
JPanel keywordPanel = new JPanel(new BorderLayout());
JPanel searchCategoryPanel = new JPanel(new BorderLayout());
// ...
mainPanel.setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5));
chooseCollectionPanel.setBorder(BorderFactory.createEmptyBorder(5, 0,
5, 0));
keywordPanel.setBorder(BorderFactory.createEmptyBorder(5, 0, 5, 0));
searchCategoryPanel.setBorder(BorderFactory.createEmptyBorder(5, 0, 5,
0));
JLabel chooseCollectionLabel = new JLabel("Choose Collection: ");
JComboBox<String> chooseCollectionCB = new JComboBox<String>();
chooseCollectionCB.addItem("All");
chooseCollectionPanel.add(chooseCollectionLabel, BorderLayout.WEST);
chooseCollectionPanel.add(chooseCollectionCB, BorderLayout.CENTER);
JLabel chooseCollectionkeywordLabel = new JLabel("Choose Collection: ");
JTextField keywordCB = new JTextField(10);
keywordPanel.add(chooseCollectionkeywordLabel, BorderLayout.WEST);
keywordPanel.add(keywordCB, BorderLayout.CENTER);
// ...
mainPanel.add(chooseCollectionPanel);
mainPanel.add(keywordPanel);
mainPanel.add(searchCategoryPanel);
// ...
frame.add(mainPanel);
frame.setSize(400, 400);
frame.pack();
frame.setVisible(true);
}
public static void main(String[] args) {
new Test();
}
}

Related

GridBagLayout Resizing Panel

I have read several articles regarding this topic as well the documentation, yet I still can't seem to solve my problem.
I have created a GUI that needs to be resizeable; however, I would like to maintain that there are 3 JTextFields for each row.
I have tried adjusting the weightx, yet no success.
Here is a snippet of my code:
JPanel panelMain = new JPanel();
getContentPane().add(panelMain);
JPanel panelForm = new JPanel(new GridBagLayout());
panelMain.add(panelForm);
//JScrollPane scrollpane = new JScrollPane(panelForm);
//panelMain.add(scrollpane);
GridBagConstraints c = new GridBagConstraints();
c.gridx = 0;
c.gridy = 0;
// Row 1
buttonAddCourses = new JButton("Add Credit Hours");
buttonAddCourses.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
c.gridx = 0;
c.weightx = 0.2;
for(int i = 0; i < 15; i++) {
JTextField newTextField = new JTextField(20);
listTextFields.add(newTextField);
panelMain.add(newTextField,c);
c.gridy++;
}
panelMain.validate();
panelMain.repaint();
}
});
panelForm.add(buttonAddCourses, c);
c.gridx++;
Before Resize : !https://imgur.com/a/XCUtJ
After Resize :
!https://imgur.com/a/a4Qo4
JPanel panelMain = new JPanel(); // <- gets a FlowLayout by default
// ..
JPanel panelForm = new JPanel(new GridBagLayout()); // panel with GBL
// ...
panelMain.add(newTextField,c); // No, this should be panelForm.add(..);

Java component positioning on GridBagLayout

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

Issue when using Java Swing in a For-Loop

I have created a simple for-loop that changes the amount of JTextFields and JLabels based on the value of a JSpinner, as seen in the following code:
public static ArrayList<ArrayList<JTextField>> ChangeQuestionAnswerFields(int numberOfQuestions){
ArrayList<ArrayList<JTextField>> txtFieldArray = new ArrayList<ArrayList<JTextField>>();
JPanel scrollPanel = new JPanel(new GridBagLayout());
//JScrollPane scrollPane = new JScrollPane(scrollPanel);
frame.add(scrollPanel, BorderLayout.CENTER);
GridBagConstraints c = new GridBagConstraints();
c.gridx = 0;
c.gridy = 0;
for(int i = 0; i != numberOfQuestions; i++){
JTextField tempQuestion = new JTextField(10);
JTextField tempAnswer = new JTextField(10);
JLabel tempQuestionHeader = new JLabel("Question " + (i + 1)+ " ");
JLabel tempQuestionLbl = new JLabel("Question: ");
JLabel tempAnswerLbl = new JLabel("Answer: ");
ArrayList<JTextField> tempArrayList = new ArrayList<>();
tempArrayList.add(tempQuestion);
tempArrayList.add(tempAnswer);
txtFieldArray.add(tempArrayList);
c.gridy++;
c.gridx = 0;
scrollPanel.add(tempQuestionHeader, c);
c.gridy++;
c.gridx = 0;
scrollPanel.add(tempQuestionLbl, c);
c.gridx = 1;
c.gridwidth = 3;
scrollPanel.add(tempQuestion, c);
c.gridy++;
c.gridx = 0;
scrollPanel.add(tempAnswerLbl, c);
c.gridx = 1;
c.gridwidth = 3;
scrollPanel.add(tempAnswer, c);
}
return txtFieldArray;
}
}
The value of the Spinner is passed into the method, and the method is called using a change listener (where noQuestions is the value of the JSpinner):
noQuestions.addChangeListener(e -> {
ChangeQuestionAnswerFields((int) noQuestions.getValue());
frame.revalidate();
frame.repaint();
});
This method is first called in the code when the screen first appears, and works properly. However, whenever the value of the spinner changes the original labels and fields stay on the screen and more text fields simply appear, or disappear on top.
http://i.imgur.com/GBY8L3u.png - JSpinner has a value of 2
http://i.imgur.com/pSQsA3G.png - JSpinner has a value of 3
Is there any way to fix this? Any help is much appreciated.
Thanks,
Tom
Minimal Runnable Example:
import javax.swing.*;
import java.awt.*;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.util.ArrayList;
public class MainGUI {
static JFrame frame = new JFrame("Math Reviser");
public static void main(String[] args) {
frame.setSize(400, 600);
frame.setVisible(true);
createScreen();
frame.revalidate();
frame.repaint();
public static void createScreen(){
frame.getContentPane().removeAll();
JSpinner noQuestions = new JSpinner(new SpinnerNumberModel(1, 1, 10, 1));
frame.add(noQuestions, BorderLayout.NORTH);
);
changeQuestionAnswerFields(1);
frame.revalidate();
frame.repaint();
noQuestions.addChangeListener(e -> {
changeQuestionAnswerFields((int) noQuestions.getValue());
frame.revalidate();
frame.repaint();
});
}
public static ArrayList<ArrayList<JTextField>> changeQuestionAnswerFields(int numberOfQuestions){
ArrayList<ArrayList<JTextField>> txtFieldArray = new ArrayList<ArrayList<JTextField>>();
JPanel scrollPanel = new JPanel(new GridBagLayout());
frame.add(scrollPanel, BorderLayout.CENTER);
GridBagConstraints c = new GridBagConstraints();
c.gridx = 0;
c.gridy = 0;
for(int i = 0; i != numberOfQuestions; i++){
JTextField tempQuestion = new JTextField(10);
JTextField tempAnswer = new JTextField(10);
JLabel tempQuestionHeader = new JLabel("Question " + (i + 1)+ " ");
JLabel tempQuestionLbl = new JLabel("Question: ");
JLabel tempAnswerLbl = new JLabel("Answer: ");
ArrayList<JTextField> tempArrayList = new ArrayList<>();
tempArrayList.add(tempQuestion);
tempArrayList.add(tempAnswer);
txtFieldArray.add(tempArrayList);
c.gridy++;
c.gridx = 0;
scrollPanel.add(tempQuestionHeader, c);
c.gridy++;
c.gridx = 0;
scrollPanel.add(tempQuestionLbl, c);
c.gridx = 1;
c.gridwidth = 3;
scrollPanel.add(tempQuestion, c);
c.gridy++;
c.gridx = 0;
scrollPanel.add(tempAnswerLbl, c);
c.gridx = 1;
c.gridwidth = 3;
scrollPanel.add(tempAnswer, c);
}
return txtFieldArray;
}
}
Using static variables and methods is an indication of a poorly designed application. There is no need for the static variables or methods. I suggest you read the section from the Swing tutorial on How to Use Labels. The LabelDemo.java code will show you how to create a panel containing all the components. This panel will then be added to the frame. This panel will also contain all the instance variables you need for your program.
Not only that the example will show you how to create the GUI components on the EDT which is something you should always do to prevent random errors since Swing was designed to be single threaded.
However, the main problem with your existing code is that you continue to create and add new panels to the content pane of the frame. Try changing the spinner to 2 and then resize the frame. Then try changing the spinner to 3 and resize the frame. After the resizing the first panel is displayed. This is because Swing will paint the last component added first so the first panel added will be painted on top of the last panel you created.
You can change this in your existing code by removing the old panel before adding the new panel:
static JFrame frame = new JFrame("Math Reviser");
static JPanel scrollPanel = new JPanel();
...
frame.remove(scrollPanel);
//JPanel scrollPanel = new JPanel(new GridBagLayout());
scrollPanel = new JPanel(new GridBagLayout());
However, I do not recommend this approach. As I initially suggestion you need to redesign the entire class. When you do the redesign I would use a BorderLayout on your panel and then you can add your spinner to the PAGE_START and then add a JScrollPane to the CENTER of the panel.
Then when you want to create a new panel you add the panel to the scrollpane using code like:
scrollPane.setViewportView( scrollPanel );
The scrollpane will refresh itself and you don't need to worry about revalidate() or repaint() or anything else.

GridBagLayout overwrting previos row

I have this piece of code to add a series of questions and 4 answers for each question.However when i add 3rd question it overwrites the previous row.Can anypne help me to solve this problem.I just need a hint about why it is doing this
package Generators;
/**
*
* #author samim
*/
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 javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTextField;
public class CreateFormFields
{
// Field members
static JPanel panel = new JPanel();
static Integer indexer = 1;
// static List<JLabel> question = new ArrayList<JLabel>();
static List<JTextField> question = new ArrayList<JTextField>();
static List<JTextField> ans1 = new ArrayList<JTextField>();
static List<JTextField> ans2 = new ArrayList<JTextField>();
static List<JTextField> ans3 = new ArrayList<JTextField>();
static List<JTextField> ans4 = new ArrayList<JTextField>();
static List<JTextField> result = new ArrayList<JTextField>();
public static void main(String[] args)
{
// Construct frame
JFrame frame = new JFrame();
frame.setLayout(new GridBagLayout());
frame.setPreferredSize(new Dimension(990, 990));
frame.setTitle("Form Creator");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
// Frame constraints
GridBagConstraints frameConstraints = new GridBagConstraints();
// Construct button
JButton addButton = new JButton("Add");
addButton.addActionListener(new ButtonListener());
// Add button to frame
frameConstraints.gridx = 0;
frameConstraints.gridy = 0;
frame.add(addButton, frameConstraints);
// Construct panel
panel.setPreferredSize(new Dimension(950, 400));
panel.setLayout(new GridBagLayout());
JScrollPane scrollPane = new JScrollPane(panel, JScrollPane.VERTICAL_SCROLLBAR_ALWAYS,
JScrollPane.HORIZONTAL_SCROLLBAR_NEVER);
scrollPane.setPreferredSize(new Dimension(900, 400));
frameConstraints.gridx = 0;
frameConstraints.gridy = 1;
frame.add(scrollPane, frameConstraints);
// Pack frame
frame.pack();
// Make frame visible
frame.setVisible(true);
}
static class ButtonListener implements ActionListener
{
#Override
public void actionPerformed(ActionEvent arg0)
{
// Clear panel
panel.removeAll();
panel.setPreferredSize(new Dimension(950,panel.getHeight()+200));
// Create label and text field
JTextField jTextField = new JTextField();
JTextField jTextField2 = new JTextField();
JTextField jTextField3 = new JTextField();
JTextField jTextField4 = new JTextField();
JTextField jTextField5 = new JTextField();
JTextField jTextField6 = new JTextField();
question.add(jTextField);
ans1.add(jTextField2);
ans2.add(jTextField3);
ans3.add(jTextField4);
ans4.add(jTextField5);
result.add(jTextField6);
// Create constraints
GridBagConstraints textFieldConstraints = new GridBagConstraints();
GridBagConstraints labelConstraints = new GridBagConstraints();
GridBagConstraints gridans1 = new GridBagConstraints();
GridBagConstraints temp = new GridBagConstraints();
// Add labels and text fields
for(int i = 1; i <= indexer; i++)
{
int R = (int) (Math.random() * 255);
int G = (int) (Math.random() * 255);
int B = (int) (Math.random() * 255);
String total = "rgb(" + R + "," + G + "," + B + ")";
// Label constraints
labelConstraints.gridx = 0;
labelConstraints.gridy = i * 7;
labelConstraints.insets = new Insets(5, 300, 5, 300);
panel.add(new JLabel("<html><font color='" + total + "'>Question " + i + "<font></html>"),
labelConstraints);
temp.gridx = 0;
temp.gridy = i * 8;
temp.insets = new Insets(10, -550, 10, 2);
panel.add(new JLabel("Question :"), temp);
// Text field constraints
textFieldConstraints.fill = 1;//GridBagConstraints.HORIZONTAL;
textFieldConstraints.insets = new Insets(10, -550, 10, 0);
textFieldConstraints.gridy = i * 8;
panel.add(question.get(i - 1), textFieldConstraints);
gridans1.fill = 1;//GridBagConstraints.HORIZONTAL;
gridans1.insets = new Insets(10, -550, 10, 290);
gridans1.gridy = i * 9;
temp.gridy = i * 9;
panel.add(new JLabel("Answer 1 :"), temp);
panel.add(ans1.get(i - 1), gridans1);
gridans1.gridy = i * 10;
temp.gridy = i * 10;
panel.add(new JLabel("Answer 2 :"), temp);
panel.add(ans2.get(i - 1), gridans1);
gridans1.gridy = i * 11;
temp.gridy = i * 11;
panel.add(new JLabel("Answer 3:"), temp);
panel.add(ans3.get(i - 1), gridans1);
gridans1.gridy = i * 12;
temp.gridy = i * 12;
panel.add(new JLabel("Answer 4:"), temp);
panel.add(ans4.get(i - 1), gridans1);
gridans1.gridy = i * 13;
temp.gridy = i * 13;
panel.add(new JLabel("Good Result :"), temp);
panel.add(result.get(i - 1), gridans1);
}
// Align components top-to-bottom
// Increment indexer
indexer++;
panel.updateUI();
}
}
}
Four GridBagConstrains, many components with looping: the performance is poor, the code read style is too complex. It is better you change your design of your code:
While working with group of similar type of component, try enclose them inside another Container Component:
MyQuestionPanel extends JPanel
{
public JLabel questionLab;
public JTextField qAnsField1;
public JTextFeild qAnsField2;
MyQuestionPanel(int questionNo)
{
setLayout(/* GridBagLayout any layout relevant to your design */);
//add your compoenents suing add(component, gridBagConstraint),
//or with relevant add() method e.g., questionLab, qAnsField1, qAnsField2
}
}
Now, assuming that you have root panel which is going to maintain the Question panel(which is container the question label and answer input field), has necessary layout(BoxLayout or other relevant to your design), create a new instance of MyQuestionPanel and add them to the root panel. You don't need to remove all the question panel and re-add them to the root panel on Button Click, as you were doing, rather simply put the root.add(MyQuestionPanel)
You don't need to call updateUI(); in fact you should not in this case, because it will first uninstall all the properties you had of the calling component and then reinstall the UI with extra call revalidate() and repaint(). Just call root.revalidate() and then root.repaint() to reflect the changes for adding and removing component.
If you do these, you will see your code will be much shorter, simpler and nicer. Give it a try.

GridBagLayout - Height of one row causes the width of next row to change

The UI I am working on displays a panel which lets a user select a movie and play. There are controls to play, pause, etc.
The layout seems to look the way I want. The panel uses a GridBagLayout. Row 2 displays a text area for status messages and row 3 displays a panel with buttons and a progress bar.
The problem I am running into is that when I have too many lines of text in the text area, the buttons in row 3 wrap around. This is irrespective of the height of the outer frame.
The height in row 2 is affecting the width in row 3. I don't understand this behavior. I am wondering if someone can tell me what is it that I am doing wrong and how I can fix it? I have attached the code.
On a slightly different topic, if you are looking at the code, can you also suggest a way to leave a margin between the bottom-most component and the outermost panel?
Thank you in advance for your help.
Regards,
Peter
private static JButton CreateImageButton(String fileName) {
JButton retVal = new JButton("xxx");
return retVal;
}
public MoviePanel() {
this.setLayout(new GridBagLayout());
this.setBackground(Color.WHITE);
JButton btnRefresh = CreateImageButton("refresh.png");
GridBagConstraints c = new GridBagConstraints();
c.gridx=0;
c.gridy=0;
c.fill = GridBagConstraints.NORTH;
c.insets.left = 10; c.insets.right = 10; c.insets.top = 10;
this.add(btnRefresh, c);
JComboBox cbMovieList = new JComboBox();
c = new GridBagConstraints();
c.gridx = 1;
c.gridy = 0;
c.fill = GridBagConstraints.HORIZONTAL;
c.insets.right = 10; c.insets.top = 10;
c.weightx = 1.0;
this.add(cbMovieList, c);
JButton btnAuthorize = new JButton("Get Info");
c = new GridBagConstraints();
c.gridx = 1;
c.gridy = 1;
c.anchor = GridBagConstraints.WEST;
c.insets.top = 10;
this.add(btnAuthorize, c);
JTextArea txtInfo = new JTextArea();
txtInfo.setFont( new Font("SansSerif", Font.BOLD, 12));
txtInfo.setBackground(Color.cyan);
// txtInfo.setText("abc\ndef");
txtInfo.setText("abc\ndef\nghi\njkl\nmno\npqr\nstu\nvwx\nyz");
c = new GridBagConstraints();
c.gridx = 1;
c.gridy = 2;
c.anchor = GridBagConstraints.NORTHWEST;
c.weighty = 1.0;
c.insets.top = 10;
this.add(txtInfo, c);
JPanel controllerOuter = new JPanel();
controllerOuter.setLayout(new BoxLayout(controllerOuter, BoxLayout.Y_AXIS));
controllerOuter.setBorder(BorderFactory.createRaisedBevelBorder());
FlowLayout controllerLayout = new FlowLayout(FlowLayout.CENTER);
controllerLayout.setHgap(0);
JPanel controller = new JPanel(controllerLayout);
controller.setBorder(new EmptyBorder(10, 10, 10, 10));
Dimension dim = new Dimension(60, 40);
JButton btnPlay = CreateImageButton("play.png");
btnPlay.setPreferredSize(dim);
controller.add(btnPlay);
JButton btnPause = CreateImageButton("pause.png");
btnPause.setPreferredSize(dim);
controller.add(btnPause);
JButton btnStop = CreateImageButton("stop.png");
btnStop.setPreferredSize(dim);
controller.add(btnStop);
JButton btnForward = CreateImageButton("forward.png");
btnForward.setPreferredSize(dim);
controller.add(btnForward);
JComboBox cbAspectRatio = new JComboBox();
cbAspectRatio.setPreferredSize(new Dimension(100, 40));
cbAspectRatio.setBorder(new EmptyBorder(0, 10, 0, 0));
controller.add(cbAspectRatio);
controllerOuter.add(controller);
JProgressBar pbProgress = new JProgressBar(0, 100);
pbProgress.setPreferredSize(new Dimension(350, 40));
pbProgress.setBorder(new EmptyBorder(0, 10, 10, 10));
pbProgress.setValue(50);
pbProgress.setString("50/100");
pbProgress.setStringPainted(true);
pbProgress.setForeground(Color.BLUE);
pbProgress.setBorderPainted(true);
controllerOuter.add(pbProgress);
c = new GridBagConstraints();
c.gridx = 0;
c.gridy = 3;
c.gridwidth = 2;
c.weightx = 1.0;
this.add(controllerOuter, c);
}
I see several things in your code:
You force the preferredSize of the JButton's. If possible, I would remove that because this will often get you more problems than solutions. If you want to force the preferredSize, you should also pay attention to set the minimum and maximum sizes as well, otherwise you get weird behaviour like the one you are observing
You use a BoxLayout to display the controls. While this is perfectly acceptable, BoxLayout also relies on min/max size to perform the layout, which you did not set.
You use imbricated layouts. This is fine too, but why not use only the GridBagLayout of your MoviePanel?
Usually TextAreas are wrapped in JScrollPane, in case the text is too big. You can also setLineWrap(true) on the TextArea, so that it does not go too far on the right. By setting rows/columns on the TextArea, you will define its preferreSize (to prevent it from depending of the text it contains).
On your GridBagConstraints, the fill property can only be: NONE, VERTICAL, HORIZONTAL or BOTH (You used VERTICAL for one of them). Also, it is not needed to recreate a new instance, you can reuse the same GridBagConstraint over and over, it is automatically cloned by the LayoutManager when you set the constraint for the component.
Now for the solutions, I found several:
When you add the contollerOuter, also specify c.fill = GridBagConstraints.HORIZONTAL; (This is the easiest way to solve your issues)
When you set the preferredSize of the JButtons, also force their minimumSize to the same value.
Use only the GridBagLayout to layout all components. (This would be my favorite)
Replace the FlowLayout by a BoxLayout with a X_AXIS.
Rember that GridBagConstraints properties :
gridx, gridy: specifies the location
gridwidth, gridheight: specifies the colspan/rowspan
weightx, weighty: specifies who gets the extra horizontal/vertical space and in what proportion
anchor: specifies the alignement of the component withing its "cell", if the "cell" is bigger than the component
fill: specifies if the component should stretch to the cell width/height
Just adding one JPanel each for Center and Bottom will do the trick for you, so till your JTextArea your GridBagLayout will server the purpose and after that the BorderLayout of the MAIN JPanel will do. Moreover, adding JScrollPane also to the whole thing reduces the effort needed at other areas. Have a look at the code and output :
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import javax.swing.border.*;
public class JTextPaneExample extends JPanel
{
private Icon info = UIManager.getIcon("OptionPane.informationIcon");
private Icon error = UIManager.getIcon("OptionPane.errorIcon");
private static JButton CreateImageButton(String fileName) {
JButton retVal = new JButton("xxx");
return retVal;
}
private void createAndDisplayGUI()
{
JFrame frame = new JFrame("JTextPane Example");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
this.setLayout(new BorderLayout());
this.setBackground(Color.WHITE);
JPanel centerPanel = new JPanel();
centerPanel.setBorder(BorderFactory.createEmptyBorder(10, 10, 10, 10));
centerPanel.setLayout(new GridBagLayout());
centerPanel.setBackground(Color.WHITE);
JButton btnRefresh = CreateImageButton("refresh.png");
GridBagConstraints c = new GridBagConstraints();
c.gridx=0;
c.gridy=0;
c.fill = GridBagConstraints.NORTH;
c.insets.left = 10; c.insets.right = 10; c.insets.top = 10;
centerPanel.add(btnRefresh, c);
JComboBox cbMovieList = new JComboBox();
c = new GridBagConstraints();
c.gridx = 1;
c.gridy = 0;
c.fill = GridBagConstraints.HORIZONTAL;
c.insets.right = 10; c.insets.top = 10;
c.weightx = 1.0;
centerPanel.add(cbMovieList, c);
JButton btnAuthorize = new JButton("Get Info");
c = new GridBagConstraints();
c.gridx = 1;
c.gridy = 1;
c.anchor = GridBagConstraints.WEST;
c.insets.top = 10;
centerPanel.add(btnAuthorize, c);
JTextArea txtInfo = new JTextArea();
txtInfo.setFont( new Font("SansSerif", Font.BOLD, 12));
txtInfo.setBackground(Color.cyan);
// txtInfo.setText("abc\ndef");
txtInfo.setText("abc\ndef\nghi\njkl\nmno\npqr\nstu\nvwx\nyz");
JScrollPane scroller = new JScrollPane();
scroller.setViewportView(txtInfo);
c = new GridBagConstraints();
c.gridx = 1;
c.gridy = 2;
c.anchor = GridBagConstraints.NORTHWEST;
c.fill = GridBagConstraints.HORIZONTAL;
c.weighty = 1.0;
c.insets.top = 10;
centerPanel.add(scroller, c);
JPanel controllerOuter = new JPanel();
controllerOuter.setLayout(new BoxLayout(controllerOuter, BoxLayout.Y_AXIS));
controllerOuter.setBorder(BorderFactory.createRaisedBevelBorder());
FlowLayout controllerLayout = new FlowLayout(FlowLayout.CENTER);
controllerLayout.setHgap(0);
JPanel controller = new JPanel(controllerLayout);
controller.setBorder(new EmptyBorder(10, 10, 10, 10));
Dimension dim = new Dimension(60, 40);
JButton btnPlay = CreateImageButton("play.png");
btnPlay.setPreferredSize(dim);
controller.add(btnPlay);
JButton btnPause = CreateImageButton("pause.png");
btnPause.setPreferredSize(dim);
controller.add(btnPause);
JButton btnStop = CreateImageButton("stop.png");
btnStop.setPreferredSize(dim);
controller.add(btnStop);
JButton btnForward = CreateImageButton("forward.png");
btnForward.setPreferredSize(dim);
controller.add(btnForward);
JComboBox cbAspectRatio = new JComboBox();
cbAspectRatio.setPreferredSize(new Dimension(100, 40));
cbAspectRatio.setBorder(new EmptyBorder(0, 10, 0, 0));
controller.add(cbAspectRatio);
controllerOuter.add(controller);
JProgressBar pbProgress = new JProgressBar(0, 100);
pbProgress.setPreferredSize(new Dimension(350, 40));
pbProgress.setBorder(new EmptyBorder(0, 10, 10, 10));
pbProgress.setValue(50);
pbProgress.setString("50/100");
pbProgress.setStringPainted(true);
pbProgress.setForeground(Color.BLUE);
pbProgress.setBorderPainted(true);
controllerOuter.add(pbProgress);
add(centerPanel, BorderLayout.CENTER);
add(controllerOuter, BorderLayout.PAGE_END);
frame.getContentPane().add(this);
frame.pack();
frame.setVisible(true);
}
public static void main(String... args)
{
SwingUtilities.invokeLater(new Runnable()
{
public void run()
{
new JTextPaneExample().createAndDisplayGUI();
}
});
}
}
Here is the output as you add more lines :

Categories