JPanel child component paint / layout problem

I had a problem with the fact that when my frame is displayed (after the login dialog box), the buttons are not in the correct position, and then in some milliseconds they go to the correct position (the center of the panel with the border mask).

- update

In my machine, this SSCCE shows a layout problem 2 out 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 appears in the upper left, and then goes to the center. Is this a Java error?

- update

SSCCE does not seem to show a problem for anyone trying. Perhaps this is a problem with the computer. I still think Java Swing creates new threads to do the mock-up. But I'm not sure.

- update

The problem occurs only with f.setExtendedState (JFrame.MAXIMIZED_BOTH);

+4
source share
8 answers

Looks like a java error. I reported this (but for some reason it still does not appear in error reports).

0
source

Your problem intrigued me. After some investigation, I think I confirmed something that I remember about setting the state of the window (maximized, restored, etc.), which is that setting the state is a request to the operating system and left whim of the OS to process the request. This means that it is asynchronous, or at least done later, after installing it. I confirmed the use of logging and the addition of resize listeners, where you can see that the frame size changes after the code exits the code. Because of this, package () will compose components at their preferred size. Imagine that the frame size is 800x600, and the components are arranged as such (the horizontal button is about 400). Then later, the OS will change the frame size to full screen (for example, 1024x768) - for a moment you will see that the button is still at 400. Then the frame processes the new size and shifts the components and centers on the button to about 512. Thus, you will see flicker when it goes during this process. Perhaps the solution is to NOT pack () - it will remain at zero and the user will see minimal flicker.

First try this change:

 // pack() 

If this looks good, then you may have the following problem ... if the user presses the restore button, the entire frame is compressed into a black hole. Therefore, try calling the packet AFTER the frame was predicted to be resized due to maximization. Something like that:

 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 the restore button, the frame will have a beautifully packaged size, ready to go.

- Update

OK, last try. Although I think my description of the problem has some truth, my proposed solutions did nothing. Here is the last attempt. Remove package () and setPreferredSize () and replace it by setting the size to the screen size. This seems to significantly reduce the flicker of my system. This is because there should be no difference between the original layout and the maximized layout done later. You can see this if you switch between recovery and maximum. Although I still see very little flicker when switching the two, at least it seems to look better when it is 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

+2
source

I would suggest that you need to call package () before making your frame visible.

If you call the above code not in the event stream, then you have a race condition and all bets are disabled - you can control the graphical interface only from the EDT (event sending stream).


EDIT: I tried my SSCCE on my system and did not show the behavior you see. I tried it about 50 times, and also tried to create 10 windows, looping your code. I am running 1.6.0_18 on Windows XP SP3.

+1
source

Perhaps you are missing frameThatContainsCentralPanel.pack() ?

+1
source

Well, if it works with SSCCE, then you have proven that the problem is not related to the underlying logic. There must be something else between SSCCE and your real code. Since we do not have access to your real code, you need to do the debugging yourself to find out what the difference is.

However, in this case, the best solution is to use CardLayout, which makes it easy to exchange panels. Read the Swing tutorial for a working example.

Or the anther approach is to use the "login dialog". After a successful login, you show your main frame with a panel for your application.

+1
source

The "then in a few milliseconds" part sounds to me as if you need to call validate() on your frame. In addition, if you use f.pack() , your panel needs a preferred size because pack() provides the parent components with their preferred sizes and resizes based on them.

+1
source

If I copied your code, I had the same problem, but not so severe.

I solved this by setting the 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 work on Linux.

This is really weird ... I'm sure it is something like the size of all the containers in a swing tree.

0
source

I would expect the frame to be maximum as before it was shown, but after checking this, I am sure that after it has been displayed, on the linux frame. You can make the frame size equal to the size of the screen before calling setVisible, or you can make the components invisible until you know that it has received its preferred initial size. Here is a modified sample that shows the elements after activating the frame (when the linux event is activated, it happens late enough not to show the "transition button"):

 import javax.swing.JButton; 

import javax.swing.JFrame; import javax.swing.JPanel; import javax.swing.SwingUtilities;

open 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); } }); } 

}

0
source

Source: https://habr.com/ru/post/1303697/


All Articles