如果显示器关闭,AWT/Swing会取消绘制操作吗?

12
我在使用Swing时遇到了一个问题,只有在计算机显示器关闭时才会出现,但我的Swing应用程序仍在后台运行。似乎每当显示器关闭时,Swing/AWT会取消所有绘制操作,导致GUI中出现一些显示问题,这些问题在显示器重新打开后立即可见。
例如,当我使用自定义的JNI函数关闭显示器,然后打开一个简单的消息对话框时,当显示器重新打开时,消息对话框是空白的。

Blank message dialog

但是在下一次重绘之后,它会正确地绘制出来。

Correctly painted message dialog

这是Swing的预期行为吗?有没有办法指示Swing在显示器关闭的情况下继续绘制到屏幕上?
编辑:这是一个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");
            }
        });
    }
}

我正在使用64位的Windows 7 Home Premium SP1和64位的Java 1.6.0_24。
编辑2:这是另一个我遇到“取消绘画操作”效果的程序。
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();
            }
        });
    }
}

在显示器关闭之前,我看到:

Screenshot of the dialog of DialogTest before the monitor shuts off

当显示器关闭时,标签的背景颜色在背景中变为黄色。然后我移动鼠标打开显示器。对话框在视觉上没有改变。只有在我强制重绘(例如通过ALT-TAB切换窗口)后,我才能看到黄色。

Screenshot of the dialog of DialogTest where the main label has a yellow background

编辑3:已向Oracle报告Bug ID 7049597


1
我无法重现这种效果。请提供一个展示问题的 sscce 。是哪个平台? - trashgod
@trashgod:提供一个SSCCE会有些困难,因为我用于关闭显示器的自定义JNI例程是专有的。不过我会尽力想办法。我正在使用64位Windows 7 Home Premium SP1和64位Java 1.6.0_24。 - Joseph Trebbien
编写最小的示例,手动启动睡眠模式时重现效果。另请参阅初始线程 - trashgod
@trashgod:我添加了一个SSCCE。为了使用它,我暂时更改了我的电源计划设置,使显示器在1分钟的不活动后关闭。然后我启动了程序并停止了移动鼠标/打字。一分钟后,屏幕关闭了。我等了另外20秒才移动鼠标。显示器重新开启,我看到一个空白的消息对话框。我ALT-TAB了一个程序在顶部,将其最小化,并看到对话框被正确绘制。 - Joseph Trebbien
2个回答

2

我启动了程序并停止了鼠标/键盘的操作。一分钟后,屏幕关闭了。我等待了另外20秒才移动鼠标。显示器重新开启,并出现了一个空白的消息对话框。

根据您的示例,在我的(非Windows)平台上看不到这个问题。您可以尝试下面的示例,它应该在唤醒时在WINDOW_ACTIVATED和休眠时在WINDOW_DEACTIVATED之间交替。如果是这样,您可以扩展JDialog并在windowActivated()中调用repaint()

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 https://dev59.com/iG025IYBdhLWcg3wLizo */
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();
            }
        });
    }
}

尽管“Dialog event test”对话框在监视器关闭时不会收到窗口停用事件,但有趣的是,如果它在监视器关闭后打开,则我看不到“取消绘画操作”的效果。然而,如果我将new DialogEventTest().display();替换为JOptionPane.showMessageDialog(null, "Test");,那么我就会看到问题。 - Joseph Trebbien
@mKorbel:谢谢!我也对并发性产生了疑问,但是这个例子似乎是正确的。@Joseph Trebbien:我模糊地记得有一个Windows特定的问题,只有在JOptionPane处于睡眠状态时才会出现。扩展JDialog是可能的解决方法吗?我猜测没有top-level container来接收WINDOW_ACTIVATED - trashgod
@mKorbel:我不这么认为。我的应用程序完全是使用Swing编写的,但我发布的SSCCE也总是有这个问题。 - Joseph Trebbien
@Joseph Trebbien 如果你想看到JOptionPane,那么允许禁用代码更改,如果(count == (myTable.getRowCount() * myTable.getColumnCount())) {变为if (count == 25 * 6)) {,并且runProcess = false;必须是If-block内的最后一行。 - mKorbel
@Joseph Trebbien:由于您的“DialogTest”在我的平台上也能正常工作,我怀疑这是一个与平台有关的问题。在您的“DialogTest”中添加一个窗口监听器,就像我“DialogEventTest”中所示,并在“windowActivated()”中调用“repaint()”。 - trashgod
显示剩余4条评论

0
问题可能更多地与屏幕开启时如何重新绘制有关,而不是在关闭时发生了什么。您可以通过运行屏幕录制器来进行检查。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接