JPanel's child components paint/layout problem - java

I'm having a problem that when my frame is shown (after a login dialog) the buttons are not on correct position, then in some miliseconds they go to the right position (the center of the panel with border layout).
-- update
In my machine, this SSCCE shows the layout problem in 2 of 10 times I run it:
import java.awt.BorderLayout;
import java.awt.Dimension;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
public class TEST {
public static void main(String[] args) throws Exception {
SwingUtilities.invokeAndWait(new Runnable() {
public void run() {
System.out.println("Debug test...");
JPanel btnPnl = new JPanel();
btnPnl.add(new JButton("TEST"));
JFrame f = new JFrame("TEST");
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
f.getContentPane().setLayout(new BorderLayout());
f.getContentPane().add(btnPnl);
f.setPreferredSize(new Dimension(800, 600));
f.pack();
f.setExtendedState(JFrame.MAXIMIZED_BOTH);
f.setVisible(true);
System.out.println("End debug test!");
}
});
}
}
The button first appers in the up-left, and then it goes to the center. Is it a java bug?
--update
Looks like the SSCCE don't show the problem for everyone that is trying.
Maybe it's my computer performance problem. I still think Java Swing is creating new threads for make the layout behind the scenes. But I'm not sure.
--update
The problem only occur with the f.setExtendedState(JFrame.MAXIMIZED_BOTH);

Your problem intrigued me. After some investigation I think I confirmed something that I recall about setting the window state (maximized, restored, etc) which is that setting the state is a request to the operating system and is left to the whim of the OS to process the request. This means it is asynchronous, or at least done later, after you set it. I confirmed using logging and adding resize listeners where you can see that the frame is resized after your block of code exits. Because of this, the pack() will layout components to their preferred size. So imagine the frame being sized to 800x600 and components positioned as such (button centered horizontally around 400). Then later, the OS changes the size of the frame to full screen (e.g. 1024x768) - for a moment, you'll see the button still at 400. Then the frame processes the new size and re-lays out components and centers the button at around 512. So you'll see the flicker as it transitions during this process. Perhaps a solution is to NOT pack() - it will remain at a size of zero and user will see minimum flicker.
Try this change first:
// pack()
If that looks good then you might have the next problem...if the user clicks the restore button, the whole frame shrinks into a black hole. So try calling pack AFTER the frame has been predictably resized due to the maximize. Something like this:
f.addComponentListener( new ComponentAdapter( ComponentEvent e ) {
public void componentResized( Component) {
if( f.getSize().getWidth() > 0 ) {
e.getComponent().removeComponentListener( this );
((JFrame)e.getComponent()).pack();
}
}
}
So if the user later clicks restore button the frame will have a nicely packed size ready to go.
--Update
OK, one last attempt. While I think my description of the problem has some truth, my solutions offered did nothing. Here's one last attempt. Remove pack() and setPreferredSize() and replace with setting the size to the screen size. This seems to reduce the flicker greatly on my system. This is because there should be no difference between the initial layout and the maximized layout done later. You can see this if you switch between restore and maximized. Although I still see a very slight flicker when switching the two, at least it seems to look better when first displayed.
import java.awt.BorderLayout;
import java.awt.Dimension;
import java.awt.Toolkit;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
public class TEST {
public static void main(String[] args) throws Exception {
SwingUtilities.invokeAndWait(new Runnable() {
public void run() {
System.out.println("Debug test...");
JPanel btnPnl = new JPanel();
btnPnl.add(new JButton("TEST"));
JFrame f = new JFrame("TEST");
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
f.getContentPane().setLayout(new BorderLayout());
f.getContentPane().add(btnPnl);
// f.setPreferredSize(new Dimension(800, 600));
// f.pack();
f.setSize( Toolkit.getDefaultToolkit().getScreenSize() );
f.setExtendedState(JFrame.MAXIMIZED_BOTH);
f.setVisible(true);
System.out.println("End debug test!");
}
});
-Mike

Maybe you are missing a frameThatContainsCentralPanel.pack()?

Well, if it works with a SSCCE, then you've proven the problem isn't with the basic logic. There must be something different between the SSCCE and your real code. Since we don't have access to your real code you need to do the debugging yourself to see what the difference is.
However, in this case a better solution is to use a CardLayout, which is designed to let you swap panels easily. Read the Swing tutorial for a working example.
Or anther approach is to use a "login dialog". Once the login is successfull, you display your main frame with the panel for your application.

I would guess that you need to call pack() before making your frame visible.
If you are calling the above code not on the event thread then you have a race condition and all bets are off - you can only manipulate the GUI from the EDT (event dispatch thread).
EDIT: I tried your SSCCE on my system and it is not exhibiting the behavior you are seeing. I tried it about 50 times, and also tried creating 10 windows by looping your code. I am running 1.6.0_18 on Windows XP SP3.

The "then in some milliseconds" part sounds to me like you need to call validate() on your frame. Also, if you use f.pack(), your panel needs a preferred size, because pack() gives the parent's components their preferred sizes and resizes based on them.

If I copied your code, I had the same problem, but not so heavy.
I solved it by setting a preferred size for your frame before packing. So:
import java.awt.Dimension;
System.out.println("Debug test...");
JPanel btnPnl = new JPanel();
btnPnl.add(new JButton("TEST"));
JFrame f = new JFrame("TEST");
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
f.getContentPane().setLayout(new BorderLayout());
f.getContentPane().add(btnPnl);
f.setPreferredSize(new Dimension(800, 600));
f.pack();
f.setVisible(true);
System.out.println("End debug test!");
I'm running on Linux.
It is indeed strange... I'm sure it is something about the size of all the containers in the swing tree.

I would expect the frame to be maximised both before it is shown, but after checking this I'm sure that on linux frame is maximised after it has been displayed. You can make the frame size equal to a Screen size before calling setVisible, or you can make the components invisible until you know that the it's got preferred initial size. Here is modified sample which shows the elements after the frame has been activated (on linux activated event comes late enough to not show the "jumping button"):
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
public class TEST {
public static void main(String[] args) throws Exception {
SwingUtilities.invokeAndWait(new Runnable() {
public void run() {
final JPanel btnPnl = new JPanel();
btnPnl.add(new JButton("TEST"));
final JFrame f = new JFrame("TEST");
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
f.setContentPane(btnPnl);
// calculate preferred size for TEST frame
// f.isDisplayable() will become true
f.pack();
// extended state, if can be applied, needs to be called after f.isDisplayable()
WindowListener maxBoth = new WindowAdapter() {
#Override
public void windowOpened(WindowEvent e) {
f.setExtendedState(Frame.MAXIMIZED_BOTH);
}
};
// after windows has been opened - maximize both
f.addWindowListener(maxBoth);
// initially hide the elements
// after maximized state has been applied show them
f.getContentPane().setVisible(false);
f.addWindowListener(new WindowAdapter() {
#Override
public void windowActivated(WindowEvent e) {
f.getContentPane().setVisible(true);
// remove this listener
f.removeWindowStateListener(this);
}
});
// set the frame visible
f.setVisible(true);
}
});
}
}

Looks like a java bug. I've reported it (but for some reason it still not show on the bugs reports).

Related

LocationRelativeTo (JAVA) is Failing

I often use:
setLocationRelativeTo(null);
In my JFrame Constructor to make the Frame appear right in the middle, but now it doesnt works. It just puts the JFrame in one corner.
If you have any idea of what this happens or if you now another way to always making the Jframe appear at the middle in any computer please tell me.
I said this because I could use
setLocation(x,y);
and just put the coordinates that will put the Jframe in the center of my screen but, for example if my PC is 1920 x 1080 the Jframe wont appear in the center in a 1280 x 720 PC.
If Im wrong in this please correct me, Im new to Java so I can miss a lot of things Bill Gates wont miss. (I now windows is not coded in Java)
For reference, here's an example that works correctly; you might compare it to your current approach. In particular,
Swing GUI objects should be constructed and manipulated only on the event dispatch thread.
Be sure to pack() the enclosing top-level container.
Invoke setVisible(true) as the last step in creating the GUI.
MCVE:
import java.awt.EventQueue;
import javax.swing.JFrame;
import javax.swing.JLabel;
/** #see https://stackoverflow.com/a/29643591/230513 */
public class Test {
private void display() {
JFrame f = new JFrame("Test");
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
f.add(new JLabel("Test", JLabel.CENTER));
f.pack();
f.setLocationRelativeTo(null);
f.setVisible(true);
}
public static void main(String[] args) {
EventQueue.invokeLater(() -> {
new Test().display();
});
}
}

bring Jinternal frame infront of everything

in this code when the new button is clicked, the Jinternalframe goes behind the button, even if i add anything else like, jlabel, jtextfield, etc, the internalframe opens behind everything. i tried the tofront() and movetofront() functions, but it doesnt seem to work. plz help, thanks.
code:
import java.awt.BorderLayout;
import java.awt.CardLayout;
import javax.swing.JFrame;
import javax.swing.JButton;
import java.awt.Color;
import java.awt.FlowLayout;
import java.awt.GridBagLayout;
import java.awt.GridLayout;
import javax.swing.*;
import java.awt.event.ActionListener;
import java.awt.event.ActionEvent;
public class Messages2 extends JFrame {
JFrame frame;
JButton button1;
public static void main(String[] args) {
Messages2 window = new Messages2();
window.frame.setVisible(true);
}
public Messages2() {
frame = new JFrame();
frame.getContentPane().setBackground(Color.WHITE);
frame.setBounds(100, 100, 220, 220);
frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
//frame.getContentPane().setLayout(new FlowLayout());
JButton btnNew = new JButton("New Message");
btnNew.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent arg0) {
JDesktopPane desktopPane = new JDesktopPane();
JInternalFrame intFrame = new JInternalFrame(
"JInternalFrame demo");
intFrame.setMaximizable(true);
intFrame.setIconifiable(true);
intFrame.setResizable(true);
intFrame.setClosable(true);
intFrame.setDefaultCloseOperation(JInternalFrame.DISPOSE_ON_CLOSE);
intFrame.setSize(320, 240);
// intFrame.pack();
intFrame.setVisible(true);
desktopPane.add(intFrame);
frame.add(desktopPane, BorderLayout.CENTER);
}
});
btnNew.setBounds(1, 35, 145, 31);
frame.getContentPane().add(btnNew);
}
}
when the new button is clicked, the Jinternalframe goes behind the button, even if i add anything else like, jlabel, jtextfield, etc,
Based on the code you posted in you last question (before you deleted it), you are attempting to add all your components directly to the frame. Swing paints components in the reverse order that a component is added. So since the internal frame is added last it is painted first and then all the other components are painted over top of it.
This is not the way you should be working with a JInternalFrame. You need to separate the JDesktopPane/JInternalFrames from your other components and work with each separately.
Read the section from the Swing tutorial on How to Use Internal Frames for basic information and examples.
the internal frame is added to a JDesktopPane which is added to the frame.
Don't use a null layout for your other components. Swing was designed to be used with layout managers. These components should be added to a panel and then the panel added to the frame.
The panel and the desktop pane must be added to different areas of the frame. For example:
frame.add(panel, BorderLayout.NORTH);
frame.add(desktopPane, BorderLayout.CENTER);
You're not using JInternalFrame correctly. It should only be placed within a JDesktopPane, something you're not doing and that I suggest that you start doing. Please check the appropriate tutorial on this.
Your button and your JInternalFrame now fill the field since you're using the JFrame's default BorderLayout. Suggestion:
First and foremost, explain fully just what exact effect/user experience you're trying to achieve. Are you sure that a JInternalFrame is your best choice here?
Learn about and use the layout managers to their best advantage.
If you're going to use a JInternalFrame, then while it's OK to add the JInternalFrame in your ActionListener, you're usually going to want to add the JDesktopPane to the GUI on GUI creation, not in the ActionListener.

Focus on component when a panel is loaded

I have a frame, where i load a panel into. It works fine, but nothing has focus when it loads. Pressing tab doesn't help. I have to use the mouse to press a textfield.
I've tried: jtextfield1.requestFocus(); and jtextfiel1.requestFocusInWindow(); But it doesn't work.
What am I doing wrong?
The constructor in the JPanel:
public OpretOpdater(BrugerHandler brugerHandler, ReklamationHandler reklamationsHandler) {
initComponents();
jTextFieldOrdnr.requestFocusInWindow();
this.brugerHandler = brugerHandler;
this.rekH = reklamationsHandler;
startUp();
}
Putting the panel in the frame in the GUI:
public static void opret(ReklamationHandler reklamationHandler) {
rHandler = reklamationHandler;
SwingUtilities.invokeLater(opret);
}
static Runnable opret = new Runnable() {
#Override
public void run() {
JFrame f = jframe;
f.getContentPane().removeAll();
JPanel opret = new OpretOpdater(bHandler, rHandler);
f.getContentPane().add(opret);
f.pack();
f.setLocationRelativeTo(null);
}
};
You should call requestFocusInWindow() only when components are visible/shown on a container or after pack() has been called and all components are added to the container or else it wont work.
Also please be sure to create Swing components on Event Dispatch Thread. If you haven't already have read on Concurrency in Swing.
The reason I mention the above is not creating and manipulating Swing components on the EDT can cause random artifacts in the code. i.e focus is not being given etc.
This code below was created to show how calling requestFocusInWindow before a component is visible will not work but calling it after its visible works as expected.
Also note that removing the SwingUtilities block will cause the requestFocusInWindow not to work as expected (i.e we might be given focus or not depending on our luck :P):
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JTextField;
import javax.swing.SwingUtilities;
public class Test {
public Test() {
JFrame frame = new JFrame();
frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
JTextField f1 = new JTextField(10);
JTextField f2 = new JTextField(10);
//f2.requestFocusInWindow(); //wont work (if uncomment this remember to comment the one after setVisible or you wont see the reults)
JButton b = new JButton("Button");
JPanel p = new JPanel();
p.add(f1);//by default first added component will have focus
p.add(f2);
p.add(b);
frame.add(p);
//f2.requestFocusInWindow();//wont work
frame.pack();//Realize the components.
//f2.requestFocusInWindow();//will work
frame.setVisible(true);
f2.requestFocusInWindow();//will work
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {//if we remove this block it wont work also (no matter when we call requestFocusInWindow)
#Override
public void run() {
new Test();
}
});
}
}
I would suggest a read on How to Use the Focus Subsystem.
Often it is nice to indicate which field you want to have focus when you create the field and not separate the code by adding the request focus when the frame becomes visible.
Take a look at Dialog Focus which has a solution that is also applicable in this case. Using this approach your code would look like:
JTextField f2 = new JTextField(10);
f2.addAncestorListener( new RequestFocusListener() );

Swing getHeight() value changing from 0 to 16

I am trying to make a box in Swing that has a label of "user", a text field for the username, and a button "sign in". This is my code
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import java.io.*;
import java.util.*;
public class Engine
{
JFrame frame;
public void go()
{
setUpGui();
userNameScreen();
}
public void setUpGui()
{
frame = new JFrame();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
public void userNameScreen()
{
JPanel background = new JPanel();
frame.getContentPane().add(background);
JLabel labelUserName = new JLabel("User:");
background.add(labelUserName);
System.out.println(labelUserName.getHeight()); // 0
JTextField textFieldUserName = new JTextField();
System.out.println(labelUserName.getHeight()); // 16
textFieldUserName.setPreferredSize(new Dimension(110,labelUserName.getHeight()));
background.add(textFieldUserName);
JButton buttonSignIn = new JButton("Sign In");
background.add(buttonSignIn);
/*
background.add(labelUserName);
background.add(textFieldUserName);
background.add(buttonSignIn);
frame.getContentPane().add(background);
*/
frame.pack();
}
}
My driver class just creates an instance of engine, then runs the method go().
I read that Swing components do not have attributes of height/width until they are added (because that is for the layout manager to decide how much room they have), so it makes sense that in the method userNameScreen(), adding in all components at the end* (commented out here) makes the textFieldUserName variable have no height.
However, you can see in that same method userNameScreen(), I have it do
System.out.println(labelUserName.getHeight());
twice. The first time, it is 0. The second, it is 16. I don't understand why the first time, it would register it as 0. It has already been added to the panel (in the line before), and there doesn't seem to be anything that would change its height between that first println() and the next. So why is the value 0 in the first one, and why does it change to 16 almost immediately after?
*I should note, when I say adding in all the stuff commented out at the end, it also includes removing/commenting out all the same commands done elsewhere in the code.
It is a side effect from not creating/modifying your Swing components on the EDT. Now the EDT is busy doing the layout while you are adding components in another thread.
Your main method should look like:
public static void main(String[] args) {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
new Engine().go();
}
});
}
I'm not sure why this is happening but may be because the addition of the component maybe on a background thread and might not have been updated till the next statement is called and its updated a few millisecs later and appears when you call it second time.

JFrame not being made at right dimensions

This code, when run, will make a window but not at the specified dimensions. What is wrong with it?
import javax.swing.*;
import java.awt.*;
public class Windowing {
void JFrame(){
JFrame frames = new JFrame("Total recall");
frames.setSize(1000,8000);
frames.setVisible(true);
frames.pack();
//Buttons push = new Buttons();
//((Buttons) push).buttons();
JTextField wager = new JTextField(1);
wager.setSize(100,200);
wager.setVisible(true);
wager.setLocation(100, 200);
frames.add(wager);
//frames.add(push);
}
}
You could remove the call to frames.pack(); it overrides the previously set frame size.
However, what you really want to do is remove the frames.setSize(1000,8000) and move frames.pack() down to the bottom of the method; that will ensure that the frame is big enough to display its contents but not too big to fit on the screen.
If you call pack before adding anything to the frame (like you are doing now), it will make the window extremely small; it's probably appearing near the upper left of your screen, but you won't notice it unless you know where to look.
It looks like you have a number of "opportunity areas" here.
To start, it seems like you set frame size to 1000x8000 because you didn't see any change right?
Secondly you call setVisible on the textField because you didn't see that either.
And finally you're setting the size of the textfield ( I guess because you're seeing it take the whole frame )
The problem here is that you have to invoke pack and setVisible at the end of the construction. Also, you have to learn how to use layout managers and frames.
Swing, is very powerful, but it is a bit hard to grasp at the beginning.
These two links will be helpful:
How to make frames
Using Layout Managers
I've changed your code and the result looks like this:
Here's the modified source code.
import javax.swing.*;
import java.awt.*;
public class Windowing {
public static void main( String [] args ) {
Windowing windowing = new Windowing();
windowing.showFrame();
}
void showFrame(){
JFrame frame = new JFrame("Total recall");
JButton push = new JButton("Push");
JTextField wager = new JTextField(15);
// Panels do have "FlowLayout"
JPanel panel = new JPanel();
panel.add(wager);
panel.add(push);
frame.add( panel );
frame.pack();
frame.setVisible(true);
}
}
Try to use setPreferredSize(Dimension) instead.

Categories