JPanel changes shape/position within JFrame when JButton is pressed. Why? - java

Basically, I have added a JPanel to a JFrame, and whenever I press the button within the JPanel (which changes the visibility of a component within the JPanel), the size of the JPanel and the location of the components change. I cant for the life of me figure out why this problem is occurring. Any help is greatly appreciated.
Google Searches
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.*;
public class Main {
private JFrame window;
private JPanel loginPanel;
private JButton loginButton;
private JTextField usernameField;
private JPasswordField passwordField;
private JLabel usernameLabel, passwordLabel, errorMessage;
public static void main(String[] args) {
Main program = new Main();
}
public Main() {
createWindow();
runProgram();
}
private void createWindow() {
window = new JFrame("Dentist Program");
window.setSize(1450, 900);
window.setVisible(true);
window.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
loginPanel = new JPanel();
loginPanel.setSize(300, 400);
loginPanel.setBorder(BorderFactory.createLineBorder(Color.BLACK));
usernameLabel = new JLabel("Username");
usernameLabel.setSize(125, 20);
usernameLabel.setLocation(loginPanel.getWidth()/2 - usernameLabel.getWidth()/2, 100);
usernameField = new JTextField();
usernameField.setSize(125, 25);
usernameField.setLocation(loginPanel.getWidth()/2 - usernameField.getWidth()/2, usernameLabel.getY() + usernameLabel.getHeight());
passwordLabel = new JLabel("Password");
passwordLabel.setSize(125, 20);
passwordLabel.setLocation(loginPanel.getWidth()/2 - passwordLabel.getWidth()/2, usernameField.getY() + usernameField.getHeight() + 10);
passwordField = new JPasswordField();
passwordField.setSize(125, 25);
passwordField.setLocation(loginPanel.getWidth()/2 - passwordField.getWidth()/2, passwordLabel.getY() + passwordLabel.getHeight());
loginButton = new JButton("Log In");
loginButton.setSize(80, 25);
loginButton.setLocation(passwordField.getX(), passwordField.getY() + passwordField.getHeight() + 15);
loginButton.addActionListener(new ActionListener()
{
#Override
public void actionPerformed(ActionEvent arg0) {
errorMessage.setText("");
loginPanel.repaint();
}
});
errorMessage = new JLabel("Username and/or password is incorrect");
errorMessage.setSize(250, 25);
errorMessage.setLocation(loginPanel.getWidth()/2 - errorMessage.getWidth()/2, loginButton.getY() + loginButton.getHeight() + 25);
errorMessage.setForeground(Color.RED);
loginPanel.add(usernameLabel);
loginPanel.add(usernameField);
loginPanel.add(passwordLabel);
loginPanel.add(passwordField);
loginPanel.add(loginButton);
loginPanel.add(errorMessage);
window.getContentPane().add(loginPanel);
window.repaint();
}
private void runProgram() {
}
}

The reason your UI changes is because the look that you think is correct (the initial look) is actually wrong, and the resized version is the "correct" version.
Typically, you'd call pack() after adding components to a container, which tries to display the contents of that container at their preferred sizes. To see the correct UI before and after the button press, following your call to repaint(), add:
window.pack();
You may think that looks terrible (which it does) but it's exactly what your code is asking for, and is therefore the "correct" version.
Take note that you are trying to set sizes with explicit calls to setSize(), which is a bad practice in Swing. At most, you should be setting preferred sizes and letting the layout manager handle the details. When you try to micromanage component sizes and locations, you can often make it look good on one platform, but irredeemably bad on others. Remember that Java is supposed to be the "write once, run anywhere" language.
When you want more control over your layout, you should explicitly specify and use a layout manager, rather than just accepting the default layout. In almost every non-trivial UI, GridBagLayout is your best bet.
Finally, you don't need to explicitly call repaint() on your UI when something changes. Typically, you'd call paint() and repaint() when you are working with graphics. Let Swing handle the painting automatically.

Related

How to add button in text field?

I'm creating a text field in java using swing components. I want to make a search text field like one appears in Mozilla or other browsers.
I have added a button in text field. I have set border layout of JTextField. everything is working fine but whenever large text is written in text field (as it reaches the given size of text field) it goes behind the button. As everyone of you must have seen, this does not occur in search bars. Text must not go behind the button rather there must be some gap between button and text.
Does anyone know how to do that?
Maybe start with something like this:
The blinking cursor is positioned at the far right of the text field.
import java.awt.*;
import java.awt.image.BufferedImage;
import javax.swing.*;
class ButtonsInTextField {
JPanel gui = new JPanel(new GridBagLayout());
JTextField textField;
ButtonsInTextField(int cols) {
JPanel textFieldWithButtonsPanel = new JPanel(new FlowLayout(
SwingConstants.LEADING, 5, 1));
textField = new JTextField(cols);
textFieldWithButtonsPanel.add(textField);
addButtonToPanel(textFieldWithButtonsPanel, 8);
addButtonToPanel(textFieldWithButtonsPanel, 16);
addButtonToPanel(textFieldWithButtonsPanel, 24);
// WARNING: Not sensitive to PLAF change!
textFieldWithButtonsPanel.setBackground(textField.getBackground());
textFieldWithButtonsPanel.setBorder(textField.getBorder());
textField.setBorder(null);
// END WARNING:
gui.add(textFieldWithButtonsPanel);
}
private final void addButtonToPanel(JPanel panel, int height) {
BufferedImage bi = new BufferedImage(
// find the size of an icon from the system,
// this is just a guess
24, height, BufferedImage.TYPE_INT_RGB);
JButton b = new JButton(new ImageIcon(bi));
b.setContentAreaFilled(false);
//b.setBorderPainted(false);
b.setMargin(new Insets(0,0,0,0));
panel.add(b);
}
public final JComponent getGui() {
return gui;
}
public final JTextField getField() {
return textField;
}
public static void main(String[] args) {
Runnable r = new Runnable() {
#Override
public void run() {
ButtonsInTextField bitf = new ButtonsInTextField(20);
JOptionPane.showMessageDialog(null, bitf.getGui());
}
};
// Swing GUIs should be created and updated on the EDT
// http://docs.oracle.com/javase/tutorial/uiswing/concurrency
SwingUtilities.invokeLater(r);
}
}
As people have noted above, it might have helped to see the code, especially the Layout manager.
However, you might try the following (if you haven't yet):
Call setColumns
http://docs.oracle.com/javase/7/docs/api/javax/swing/JTextField.html#setColumns(int)
Call setPreferredSize /setMaximumSize/setMinimumSize depending on your layout manager.
But I'd try to avoid this solution because it's pixel-level maintenance.
Regards
As an alternative solution you can use a Component Border, which allows you to use the button as a Border so it appears within the text field.

Java buttons repaint

look at this simple code:
Main.java :
package CarManager;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.ImageIcon;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
public class Main extends JFrame {
private static final long serialVersionUID = 1L;
static int width = 400;
static int height = width / 16 * 9;
static String title = "Car Manager";
JButton viewTables = new JButton("View tables");
JButton clients = new JButton("Clients");
JButton search = new JButton("Search");
JButton viewCars = new JButton("View all");
JButton viewRent = new JButton("Rent a car");
JButton viewBuy = new JButton("Buy a car");
JButton viewAccessory = new JButton("Accessory");
public Main() {
setLayout(null);
setLocationRelativeTo(null);
setTitle(title);
setSize(width, height);
setResizable(false);
setDefaultCloseOperation(EXIT_ON_CLOSE);
setVisible(true);
JLabel background = new JLabel(new ImageIcon("res\\background2.jpg"));
add(background);
background.setSize(width, height);
add(viewTables);
add(clients);
add(search);
viewTables.setBounds(20, 20, 110, 30);
clients.setBounds(20, 70, 110, 30);
search.setBounds(20, 120, 110, 30);
viewTables.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
add(viewCars);
viewCars.setBounds(260, 20, 110, 20);
add(viewRent);
viewRent.setBounds(260, 50, 110, 20);
add(viewBuy);
viewBuy.setBounds(260, 80, 110, 20);
add(viewAccessory);
viewAccessory.setBounds(260, 110, 110, 20);
}
});
viewCars.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
View view = new View();
view.addWindowListener(new WindowPlug(Main.this));
setVisible(false);
}
});
}
public static void main(String args[]) {
new Main();
}
}
View.java:
package CarManager;
import javax.swing.ImageIcon;
import javax.swing.JFrame;
import javax.swing.JLabel;
public class View extends JFrame {
private static final long serialVersionUID = 1L;
int width = 400;
int height = width / 16 * 9;
String title = "View all Cars";
public View() {
setLayout(null);
setLocationRelativeTo(null);
setTitle(title);
setSize(width, height);
setResizable(false);
setDefaultCloseOperation(DISPOSE_ON_CLOSE);
setVisible(true);
JLabel background = new JLabel(new ImageIcon("res\\background2.jpg"));
add(background);
background.setSize(width, height);
}
}
and WindowPlug.java:
package CarManager;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
public class WindowPlug extends WindowAdapter {
private Main mainFrame;
public WindowPlug(Main mainFrame) { // when creating an instance of this
// WindowAdapter, tell it with which
// Main Window you are working with
this.mainFrame = mainFrame;
}
public void windowClosing(WindowEvent e) {
mainFrame.setVisible(true);
mainFrame.revalidate();
}
}
when i click view tables and then view all (those are the buttons that work for now)
and the first window hides and a new window appears, now when i close the second window the first one appears visible but the buttons are not visible, i have to hover over with the mouse for them to be visible again. ive tried mainFrame.revalidate(); and
mainFrame.repaint();
but no result
im using windows 8.1 pro
One problem with your code, and I'm not sure if this is the main problem since your code works fine on my system, is that you're calling setVisible(true) on your main window before you've added all your components. It should only be called after all components have been added.
Other problems unrelated to your main question:
You should avoid using null layout. While using null layout may seem to a newbie the better way to create complex GUI's, it's a fallacy, and more you create Swing GUI's the more you learn to respect and use the layout managers and see that these creatures help immensely in creating flexible, beautiful and if need be, complex GUI's. Then you can let them size them selves appropriately by calling pack() prior to setting them visible.
It appears that you really want to use a CardLayout to swap views on one GUI rather than spitting multiple GUI's at the user.
If you absolutely must display a dialog window, then you should use a JDialog, not a JFrame. If you used a modal JDialog, you wouldn't be needing a WindowListener.
Edit
OK, a big problem I see is that you're using null layout and adding a JLabel that covers the whole contentPane, and then adding components to the same contentPane.
Instead, make the JLabel your contentPane, and then add your JButtons, etc to it.
But make sure that the JLabel's opaque property is set to true first.
Edit 2
If you need to use an image as a background image you can:
Put the Image in an ImageIcon, put the Icon in a JLabel, and again use the JLabel as your contentPane. Again, you will need to make the JLabel opaque by calling setOpaque(true) on it. This works well if you don't want to change the size of the image or the window.
If you do need to change the size of the image, better to have a JPanel draw the image in its paintComponent(Graphics g) method, and then use this JPanel as your contentPane.
Once you've created your contentPane, then set its layout and add your components to it.
Then call setContentPane(newContentPane) on your top level window and pass in the new contentPane.

why the last GUI element i declare is filling the whole panel?

public class add extends JPanel {
private JPanel add = new JPanel();
JFrame frame;
public add(){
frame = new JFrame("Add");
frame.setBounds(550, 300, 700,500);
frame.setVisible(true);
JLabel nameLabel = new JLabel("Name");
final JTextField nameField = new JTextField();
frame.add(nameLabel);
frame.add(nameField);
nameLabel.setBounds(200, 40, 150, 30);
nameField.setBounds(350, 40, 150, 30);
...
JButton registerButton = new JButton("Salveaza");
frame.add(registerButton);
registerButton.setBounds(200,300,300, 30);
registerButton.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent ae) {
}
});
}
}
and if i delete the JButton, than a label, which would be the last element, will fill the whole JPanel when i run the programm.
what can i do so i can make it work properly?
You're adding components to a container, the JFrame's contentPane, that uses a BorderLayout as its default layout manager. When you do this and don't specify constants, the component gets added BorderLayout.CENTER, covering up any components added before.
Good Solution: learn and use layout managers, including using nested JPanels, each using its own layout manager and components.
Bad Solution: use null layout with absolute positioning. While to a newbie this seems the best way to create complex GUI's, the more you deal with Swing GUI creation, the more you will find that doing this will put your GUI in a straight-jacket, painting it in a very tight corner and making it very hard to extend or enhance. Just don't do this.

Code Executing in different orders, Jframe, Button and TextArea

Basically i have some code to make an interface that allows me to submit a request and it pulls the necessary information from a txt File. For some reason wheni execute my StartUp for the code, sometimes the button isnt there, one text box dominates the screen, all the textboxes overlap... Its weird.
Anyway heres the GUI Code
public class Menu {
SubmitCode submit = new SubmitCode();
public static JFrame frame;
public static JTextField field;
public static Button btn;
public static TextArea txtComm;
public static TextArea txtSites;
public static TextArea txtProg;
public static Dimension dim = new Dimension(40, 10);
public Menu() {
frame = new JFrame();
frame.setTitle("Welcome :)");
frame.pack();
frame.setResizable(false);
frame.setVisible(true);
frame.setLocationRelativeTo(null);
}
public static void open() {
Menu.main(null); // Opens up the main method of the class
}
public static void main(String args[]) {
field = new JTextField();
btn = new Button();
txtComm = new TextArea();
txtSites = new TextArea();
txtProg = new TextArea();
field.setText("What do you want to do?");
field.setSize(390, 20);
field.setLocation(0, 125);
btn.setVisible(true);
btn.setLabel("Click to Submit");
btn.setSize(90, 20);
btn.setLocation(400, 125);
txtComm.setVisible(true);
txtComm.setText("Commands: ");
txtComm.setSize(150, 100);
txtComm.setLocation(10, 10);
txtComm.setEditable(false);
frame.add(txtComm);
txtSites.setVisible(true);
txtSites.setText("Sites: ");
txtSites.setSize(150, 100);
txtSites.setLocation(170, 10);
txtSites.setEditable(false);
frame.add(txtSites);
txtProg.setVisible(true);
txtProg.setText("Programmes: ");
txtProg.setSize(150, 100);
txtProg.setLocation(330, 10);
txtProg.setEditable(false);
frame.add(txtProg);
frame.setSize(500, 175);
frame.add(field, BorderLayout.SOUTH);
frame.add(btn);
btn.addActionListener( new ActionListener()
{
public void actionPerformed(ActionEvent e)
{
System.out.println("Do Something Clicked");
SubmitCode.main(null);
}
});
}
}
Suggestions:
Don't use static methods/fields, except when a specific need arises or for the main method. You do not have the need here.
Instead use valid classes, classes with constructors, instance (non-static) fields and instance methods.
Don't needlessly mix AWT and Swing components but instead use just Swing components. So JTextArea, not TextArea, JButton, not Button, etc....
For instance, your Menu constructor is wasted code that is never called due to your misuse and over-use of statics.
Don't set sizes, use null layouts and absolute positioning such as with setBounds.
Instead read up on and use the layout managers.
Don't pepper your code with useless bits such as most of your calls to setVisible(true).
Call setVisible(true) on the top level window, here your JFrame, after adding all components.
Do read the relevant tutorials as this is all very well explained there. Google Java Swing Tutorials and check the very first hit.
This bit scares me: SubmitCode.main(null); and suggests that you're trying to call the static main method of another class from within a GUI. You should avoid doing this, and instead have your SubmitCode class use good OOP techniques including non-static methods and fields, constructors, etc...

The same Java-source leading to various results

I'm trying to learn Java and want to program a GUI. Trying to start with a simple calculator I ran into a problem while experimenting with JTextField. Compiling the following code sometimes results in an empty frame and sometimes in one with the panel and buttons in it.
This happens no matter if I use javac or eclipse.
I use the following code:
import java.awt.*;
import javax.swing.*;
import java.awt.event.*;
public class Mainframe extends JFrame {
private JButton button1;
private JPanel mainpanel;
private JLabel headline;
private Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize();
JTextField number1;
JTextField number2;
public Mainframe(String s) {
super(s);
setSize(screenSize.width, screenSize.height);
setVisible(true);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
mainpanel = new JPanel(new BorderLayout(100, 100));
number1 = new JTextField(20);
number2 = new JTextField(20);
headline = new JLabel("Simple Calculator");
button1 = new JButton("1");
mainpanel.add(headline, BorderLayout.NORTH);
mainpanel.add(number1, BorderLayout.SOUTH);
mainpanel.add(button1, BorderLayout.EAST);
mainpanel.add(number2, BorderLayout.CENTER);
mainpanel.validate();
getContentPane().add(mainpanel);
}
public static void main(String[] args) {
Mainframe mainframe1 = new Mainframe("Title");
}
}
I want to repeat: This is not about differences between javac and eclipse but about a problem/occurence I have with both compilers - sometimes the result is as wanted, sometimes not. Why does that happen?
Thanks in advance!
Make sure that the call to JFrame.setVisible(true) is the last line you invoke.
So just move the line:
setVisible(true);
to the end of your constructor.
A few more remarks:
Don't extend if not needed (here it is not needed)
Make sure that all the UI-related operations are done on the EDT (use a SwingUtilities.invokeLater() block to start your UI)
Don't call setSize(screenSize.width, screenSize.height); but frame.setExtendedState(Frame.MAXIMIZED_BOTH); to make a window take the whole screen space. Consider also calling pack() before making the frame visible (but before calling frame.setExtendedState(Frame.MAXIMIZED_BOTH);) so that all components are properly laid out and this will also set the size of the frame to its computed preferred size.
Move your setVisible( true ) to a moment where you have added all your components.
If you do not do this, you need to re-validate the layout as indicated in the javadoc of the Container#add method:
This method changes layout-related information, and therefore, invalidates the component hierarchy. If the container has already been displayed, the hierarchy must be validated thereafter in order to display the added component.

Categories

Resources