I am trying to create a login page. I wrote code for two textboxes and one button. One textbox next to Username and other one next to Password. One "Sign In" button below. But I am not sure why the textbox's and button are not shown on my output. I only get the Username and password label's on my ouput screen.
Strange thing is whenever I stretch my output frame, (I mean either pulling the screen horizontally or vertically) the two textboxes and the button shows up.
Please check my code and let me know what's wrong. I was trying to put pictures to make easier to understand but I do not have enough reputation. Please help.
import javax.swing.*;
import java.awt.*;
public class HomeScreen{
public static void main(String args[]){
JFrame frame = new JFrame("Medical Store");
frame.setVisible(true);
frame.setSize(600,400);
JPanel panel = new JPanel(new GridBagLayout());
frame.getContentPane().add(panel, BorderLayout.NORTH);
GridBagConstraints c = new GridBagConstraints();
JLabel label1 = new JLabel("Username");
c.gridx = 0;
c.gridy = 0;
c.insets = new Insets(10,10,10,10);
panel.add(label1,c);
JLabel label2 = new JLabel("Password");
c.gridx = 0;
c.gridy = 1;
panel.add(label2,c);
JTextField textbox1 = new JTextField(10);
c.gridx = 1;
c.gridy = 1;
panel.add(textbox1,c);
JTextField textbox2 = new JTextField(10);
c.gridx = 2;
c.gridy = 1;
panel.add(textbox2,c);
JButton button1 = new JButton("Sign In");
c.gridx = 1;
c.gridy = 2;
panel.add(button1,c);
}
}
You're calling setVisible(true) before adding all components, and so your GUI is doing just that, drawing itself before components are added.
JFrame frame = new JFrame("Medical Store");
frame.setVisible(true);
// all components added here
Solution: make the setVisible(true) call at the end after adding all components.
JFrame frame = new JFrame("Medical Store");
// all components added here
frame.setVisible(true);
Now all components should be visualized.
Other quibbles:
Avoid calling setSize(...) on anything. Instead let the layout managers and component preferred sizes do that for you.
Call pack() on the JFrame just prior to setting it visible so that the above will happen.
Related
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:
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.
I'm not really used to Java and Swing, but I need an answer for a school project :)
I have a JButton that is stretched to it's parents width/height via GirdbagLayout:
frame = new JFrame();
frame.setResizable(false);
frame.setExtendedState(JFrame.MAXIMIZED_BOTH);
frame.setUndecorated(true);
Container contentPane = frame.getContentPane();
contentPane.setLayout(new GridBagLayout());
GridBagConstraints c = new GridBagConstraints();
contentPane.setVisible(true);
JButton test = new JButton("TEST");
c.gridx = 0; c.gridy = 0; c.ipadx = 30; c.ipady = 30; c.weightx = 1; c.weighty = 1; c.fill = GridBagConstraints.BOTH;
test.setVisible(true);
contentPane.add(test, c);
frame.setVisible(true);
Now, I need to get the button's width. The reason: The Button's font-size is calculated relative to the button's size (for this calculation its width is needed.).
System.out.println("BUTTON WIDTH "+test.getWidth());
test.getWidth() is zero :(
(this is called after pane, frame and Button were set visible).
What can I do :)
Thx in advance
UPDATE:
As suggested by Yohan Danvin, I used frame.pack().
But the behavior becomes a bit strange: As if the size-change would be animated (cfr. css-transitions - that's where I sometimes get similiar problems), it changes within about 30ms:
frame.pack();
frame.setVisible(true);
System.out.println(test.getWidth());
for(int i=0; i<10; i++){
try{
Thread.sleep(10);
System.out.println(test.getWidth());
} catch(Exception err){}
}
The first and second output is "93", the 9 other ones "1600" (what would be correct).
What happens in this time? Why changes the width?
Looking forward to anyone to enlighten me :)
UPDATE:
This way, it works, the correct width is calculated:
import javax.swing.*;
import java.awt.*;
public class VIEW{
private JFrame frame;
public VIEW(){
frame = new JFrame();
frame.setResizable(false);
frame.setExtendedState(JFrame.MAXIMIZED_BOTH);
frame.setUndecorated(true);
Container contentPane = frame.getContentPane();
contentPane.setLayout(new GridBagLayout());
contentPane.setVisible(true);
GridBagConstraints c = new GridBagConstraints();
JButton test = new JButton("TEST");
c.gridx = 0; c.gridy = 0; c.ipadx = 30; c.ipady = 30; c.weightx = 1; c.weighty = 1; c.fill = GridBagConstraints.BOTH;
test.setVisible(true);
contentPane.add(test, c);
frame.pack();
frame.setVisible(true);
System.out.println(this.getWidth(test));
}
private int getWidth(JButton button){
try{
int i = 0, width = 0;
while(i++ < 10 && (width = button.getWidth()) < 100)
Thread.sleep(10);
return width;
} catch(Exception err){
return 0;
}
}
}
But of course it's a bit hacky to wait using Thread.sleep :) (and exspecially to wait till the value is bigger than 100... - this might only fit for this example and maybe even only for my screen resolution.)
Feel free to copy this class into your IDE and try it out :)
FINAL UPDATE:
SwingUtilities.invokeLater(new Runnable(){
public void run(){
System.out.println(test.getWidth());
}
});
==> waits for the window to maximize. perfect.
Problem solved :)
The problem is the size for the button has not been computed yet.
Try calling:
frame.pack();
before making the frame visible, then get the width.
UPDATE:
I think you're getting this issue because you use frame.setExtendedState(JFrame.MAXIMIZED_BOTH);, which unfortunately is not taken into consideration at the beginning (= by .pack()).
I think you have no choice than to wait for the window to be fully maximized before you can get the right value.
Use SwingUtilities.invokeLater(/*get the width here*/); instead of your custom thread sleeps. This is a more standard way to have code run after all OS events (including the maximization of the window I'm thinking) have been taken care of.
I have a class called "Console", with the following structure:
public Console(Game game) {
super(new GridBagLayout());
m_game = game;
textField = new JTextField(20);
textField.addActionListener(this);
textArea = new JTextArea(20, 75);
textArea.setEditable(false);
JScrollPane scrollPane = new JScrollPane(textArea);
GridBagConstraints c = new GridBagConstraints();
c.gridwidth = GridBagConstraints.REMAINDER;
c.fill = GridBagConstraints.HORIZONTAL;
c.fill = GridBagConstraints.BOTH;
c.weightx = 1.0;
c.weighty = 1.0;
add(scrollPane, c);
add(textField, c);
}
And then a method in my Game class:
public void createAndShowGUI() {
JFrame frame = new JFrame("My game");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(newConsole);
frame.pack();
frame.setVisible(true);
frame.setResizable(false);
}
The problem I'm having, is I don't want the Scroll Pane to be able to scroll horizontally, only vertically. The horizontal scrollbar appears when I append something to the Text Area that's too large to fit in the window. Is there a way I can prevent horizontal scrolling, and instead just have the Text Area print out whatever's too large to fit on the next line?
Example:
(The example Text Area can only fit 20 characters before it needs to allow horizontal scrolling)
Instead of
Hello, my name is Bob.
This would appear
Hello, my name is B
ob.
You can set JScrollPane.setHorizontalScrollBarPolicy(JScrollPane.HORIZONTAL_SCROLLBAR_NEVER) to ensure there is never a horizontal scroll. In addition, your textArea will need JTextArea.setWrapStyleWord(true) and JTextArea.setLineWrap(true);
I have a JPanel with the following code:
JPanel pane = new JPanel();
pane.setLayout(new GridLayout(3, 2, 10, 30));
final JTextField fileName = new JTextField();
pane.add(fileName);
JButton creater = new JButton("Create File");
pane.add(creater);
JButton deleter = new JButton("Delete File");
pane.add(deleter);
I was wondering, how do I make it so that the JTextField takes up two spaces on the GridLayout, while having the two buttons share a row by taking up one space each on the same line?
It is a hard to do with GridLyout. You have create wider cells (e.g. new GridLayout(2, 2, 10, 30), then add TextField to the fist cell. Then you have to create yet another panel with GridLayout(2, 1), put it into the cell in second line and add your button into 1 st cell of this nested grid layout.
Shortly you need GridLayout into other GridLayout.
There are better tools to implement this. First take a look on GridBagLayout. It is just to be sure that life is not always pick-nick :). Then take a look on alternative solutions like MigLayout. It is not a part of JDK but it really powerful tool that makes your life easier.
You cannot do column spans with GridLayout. I recomend you try GridBagLayout and GridBagConstraints
After trashing the suggestion of 3rd party layouts, and since I possess a malevolent hatred of GBL, I thought it was about to time to 'put my code where my mouth is' for public scrutiny (and trashing).
This SSCCE uses a nested layout.
import java.awt.*;
import javax.swing.*;
import javax.swing.border.*;
class SimpleLayoutTest {
public static void main(String[] args) {
Runnable r = new Runnable() {
public void run() {
JPanel ui = new JPanel( new BorderLayout(20,20) );
// I would go for an EmptyBorder here, but the filled
// border is just to demonstrate where the border starts/ends
ui.setBorder( new LineBorder(Color.RED,15) );
// this should be a button that pops a JFileChooser, or perhaps
// a JTree of the existing file system structure with a JButton
// to prompt for the name of a new File.
final JTextField fileName = new JTextField();
ui.add(fileName, BorderLayout.NORTH);
JPanel buttonPanel = new JPanel(new GridLayout(1, 0, 10, 30));
ui.add(buttonPanel, BorderLayout.CENTER);
JButton creater = new JButton("Create File");
buttonPanel.add(creater);
JButton deleter = new JButton("Delete File");
buttonPanel.add(deleter);
JOptionPane.showMessageDialog(null, ui);
}
};
SwingUtilities.invokeLater(r);
}
}
Take a look at the tutorial on How to Use GridBagLayout.
Sample code:
JPanel pane = new JPanel();
GridBagLayout gridbag = new GridBagLayout();
pane.setLayout(gridbag);
GridBagConstraints c = new GridBagConstraints();
final JTextField fileName = new JTextField();
c.fill = GridBagConstraints.HORIZONTAL;
c.gridwidth = 2;
c.gridx = 0;
c.gridy = 0;
pane.add(fileName, c);
JButton creater = new JButton("Create File");
c.fill = GridBagConstraints.HORIZONTAL;
c.gridwidth = 1;
c.gridx = 0;
c.gridy = 1;
pane.add(creater, c);
JButton deleter = new JButton("Delete File");
c.fill = GridBagConstraints.HORIZONTAL;
c.gridx = 1;
pane.add(deleter, c);