I am experiencing a problem with Swing that only occurs when the computer monitor is powered off, but my Swing application continues to run in the background. It seems that whenever the monitor is off, Swing/AWT cancels all painting operations, leading to a number of display issues in the GUI that are visible as soon as the monitor turns back on.
For example, when I turn off the monitor using a custom JNI function and subsequently open a simple message dialog, the message dialog is blank when the monitor turns back on:
But it paints correctly after the next repaint:
Is this the expected behavior of Swing? Is there a way to instruct Swing to continue drawing to the screen even if the monitor is powered off?
EDIT: Here is an SSCCE:
package test;
import javax.swing.JOptionPane;
import javax.swing.SwingUtilities;
public class App {
public static void main(String[] args) throws Throwable {
System.out.println("***** Please turn off the monitor in the next 70 seconds *****");
Thread.sleep(1000L * 70);
SwingUtilities.invokeLater(new Runnable() {
@Override
public void run() {
JOptionPane.showMessageDialog(null, "Test");
}
});
}
}
I am using 64-bit Windows 7 Home Premium SP1 and 64-bit Java 1.6.0_24.
EDIT 2: Here is another program with which I experience the effect of "canceled painting operations":
package test;
import static com.mycompany.Util.turnOffMonitors;
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Container;
import java.awt.Dimension;
import javax.swing.JDialog;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.SwingUtilities;
public class DialogTest extends JDialog {
private final JLabel label;
public DialogTest() {
setDefaultCloseOperation(JDialog.DISPOSE_ON_CLOSE);
label = new JLabel("Test", JLabel.CENTER);
label.setOpaque(true);
Container contentPane = getContentPane();
contentPane.setLayout(new BorderLayout());
contentPane.add(BorderLayout.CENTER, label);
this.setPreferredSize(new Dimension(200, 110));
pack();
setLocationRelativeTo(null);
setVisible(true);
Thread t = new Thread() {
@Override
public void run() {
turnOffMonitors();
try {
Thread.sleep(3000L);
} catch (InterruptedException ex) { }
SwingUtilities.invokeLater(new Runnable() {
@Override
public void run() {
label.setBackground(Color.YELLOW);
}
});
}
};
t.start();
}
public static void main(String[] args) throws Throwable {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
new DialogTest();
}
});
}
}
Before the monitor shuts off, I see:
With the monitor off, the label background color is changed to yellow in the background. I then move the mouse to turn the monitor back on. The dialog is visually unchanged. It is only after I force a repaint (by ALT-TABbing, for example) do I see the yellow:
EDIT 3: Reported to Oracle as Bug ID 7049597.
The Paint Method. When AWT invokes this method, the Graphics object parameter is pre-configured with the appropriate state for drawing on this particular component: The Graphics object's color is set to the component's foreground property. The Graphics object's font is set to the component's font property.
The main difference between AWT and Swing in Java is that AWT is Java's original platform dependent windowing, graphics and user interface widget toolkit while Swing is a GUI widget toolkit for Java that is an extension of AWT.
The painting method you're most likely to override is paintComponent . It's one of three methods that JComponent objects use to paint themselves. The three methods are invoked in this order: paintComponent — The main method for painting. By default, it first paints the background if the component is opaque.
In Java Swing, we can change the paintComponent() method instead of paint() method as paint calls paintBorder(), paintComponent() and paintChildren() methods. We cannot call this method directly instead we can call repaint(). repaint(): This method cannot be overridden. It controls the update() -> paint() cycle.
I then started the program and stopped moving the mouse/typing. After one minute, the screen turned off. I waited another 20 seconds to move the mouse. The monitor turned back on and I saw a blank message dialog.
Using your example, I don't see this on my (non-Windows) platform. You might try the example below, which should alternate between WINDOW_ACTIVATED
on wake and WINDOW_DEACTIVATED
on sleep. If so, you could extend JDialog
and repaint()
in windowActivated()
.
import java.awt.EventQueue;
import java.awt.GridLayout;
import java.awt.event.ActionEvent;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import javax.swing.AbstractAction;
import javax.swing.JButton;
import javax.swing.JDialog;
import javax.swing.JLabel;
/** @see http://stackoverflow.com/questions/6163606 */
public class DialogEventTest extends JDialog {
public DialogEventTest() {
this.setLayout(new GridLayout(0, 1));
this.add(new JLabel("Dialog event test.", JLabel.CENTER));
this.add(new JButton(new AbstractAction("Close") {
@Override
public void actionPerformed(ActionEvent e) {
DialogEventTest.this.setVisible(false);
DialogEventTest.this.dispatchEvent(new WindowEvent(
DialogEventTest.this, WindowEvent.WINDOW_CLOSING));
}
}));
}
private static class WindowHandler extends WindowAdapter {
@Override
public void windowActivated(WindowEvent e) {
System.out.println(e);
}
@Override
public void windowDeactivated(WindowEvent e) {
System.out.println(e);
}
}
private void display() {
this.setDefaultCloseOperation(JDialog.DISPOSE_ON_CLOSE);
this.addWindowListener(new WindowHandler());
this.pack();
this.setLocationRelativeTo(null);
this.setVisible(true);
}
public static void main(String[] args) {
EventQueue.invokeLater(new Runnable() {
@Override
public void run() {
new DialogEventTest().display();
}
});
}
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With