The first pass appears to either invalidate the drawable area or draws the background. And the second pass renders the menu. If there is any delay (as the example below exaserbates) then you get a grey square flickering effect.
This is JDK8 on Linux.
How can I stop this flicker effect?
public class MenuTester {
public static void main(String[] args) {
final JFrame frame = new JFrame();
frame.setBounds(100, 100, 300, 200);
final JButton button = new JButton("Show Menu");
button.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
final JPopupMenu popupMenu = new JPopupMenu();
popupMenu.add(new JMenuItem("aaaa"));
popupMenu.add(new JMenuItem("bbbb"));
popupMenu.add(new JMenuItem("cccc"));
popupMenu.setLocation(100, 100);
popupMenu.setVisible(true);
try {
Thread.sleep(2000); // Leave enough time to clearly see the ?invalidated/background? area.
} catch (InterruptedException ex) {
// Nothing to do
}
SwingUtilities.invokeLater(new Runnable() {
@Override
public void run() {
// Hide after 1 second
try {
Thread.sleep(1000);
} catch (InterruptedException ex) {
// Nothing to do
}
popupMenu.setVisible(false);
}
});
}
});
frame.add(button);
frame.setVisible(true);
}
}
Swing has, for as long as I remember it, had a "delay" when showing windows, this might have do with the time between the frame been realised by the OS and connection of the native message and event queues, but this is pure observation
I took you code and by simply by wrapping the frame's creation into a EventQueue.invokeLater
was able to get a similar behaviour
You will get different results on different systems depending on there system up and configurations
What is the event that causes the window to be rendered in the first pass?
All I did was took your code and wrapped the creation the UI in an EventQueue.invokeLater
, for example...
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JMenuItem;
import javax.swing.JPanel;
import javax.swing.JPopupMenu;
import javax.swing.SwingUtilities;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
public class MenuTester {
public static void main(String[] args) {
new MenuTester();
}
public MenuTester() {
EventQueue.invokeLater(new Runnable() {
@Override
public void run() {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
ex.printStackTrace();
}
final JFrame frame = new JFrame();
frame.setBounds(100, 100, 300, 200);
final JButton button = new JButton("Show Menu");
button.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
final JPopupMenu popupMenu = new JPopupMenu();
popupMenu.add(new JMenuItem("aaaa"));
popupMenu.add(new JMenuItem("bbbb"));
popupMenu.add(new JMenuItem("cccc"));
popupMenu.setLocation(100, 100);
popupMenu.setVisible(true);
// try {
// Thread.sleep(2000); // Leave enough time to clearly see the ?invalidated/background? area.
// } catch (InterruptedException ex) {
// // Nothing to do
// }
// SwingUtilities.invokeLater(new Runnable() {
// @Override
// public void run() {
// // Hide after 1 second
// try {
// Thread.sleep(1000);
// } catch (InterruptedException ex) {
// // Nothing to do
// }
// popupMenu.setVisible(false);
// }
// });
}
});
frame.add(button);
frame.setVisible(true);
}
});
}
}
And is there a way to render the window as transparent during that first pass and opaque during the 2nd?
This is not a new problem, this has been the state of affair since I started with Swing at Java 1.3. What you're asking would mean you knew when the paint pass was done AND was complete. Swing isn't altogether stupid, it can make some clever decisions in order to optimise the rendering process (like not painting components which are visible)
The other problem is, with a JPopupMenu
, you don't actually know if it's been displayed in a window or not (or just been displayed as a component on the glass pane for example) so the whole thing is woefully complicated