我有一个 Swing 计时器 ( javax.swing.Timer ),用于在自定义 Swing 组件中执行一些动画。
但是,这会导致问题 - 特别是即使在所有窗口都关闭之后,它似乎也会因为实时计时器线程而停止应用程序终止。当看不到动画时,避免在隐藏对象上运行计时器的开销也会很好。
理想情况下,我想做以下事情:
- 组件隐藏时停止计时器
- 只要组件变得可见,就重新开始计时
这可能吗(当然是以线程安全的方式!)
我有一个 Swing 计时器 ( javax.swing.Timer ),用于在自定义 Swing 组件中执行一些动画。
但是,这会导致问题 - 特别是即使在所有窗口都关闭之后,它似乎也会因为实时计时器线程而停止应用程序终止。当看不到动画时,避免在隐藏对象上运行计时器的开销也会很好。
理想情况下,我想做以下事情:
这可能吗(当然是以线程安全的方式!)
我对你的第一个前提持怀疑态度:这个简单的反例表明运行javax.swing.Timer
并不排除EXIT_ON_CLOSE
. package-private, sharedjavax.swing.TimerQueue
启动一个守护线程,它允许Program Exit。您可能不愿意依赖此实现细节,这是可以理解的,但可能值得寻找程序无法退出的另一个原因。
如果遵循@kleopatra on AncestorListener
; 它应该允许您Timer
根据需要控制。组件动画的占空比通常相当轻,通常以渲染为主;当组件不可见时,后者的开销很小。可能值得进行分析以验证建议的优化是否值得付出努力。如果是这样,请考虑WindowListener
在非活动或图标化窗口中最小化活动。
附录:一个现已删除的答案建议覆盖setVisible()
以调节计时器。虽然表面上很吸引人,但这种方法很脆弱并且扩展性很差。侦听器方法利用了Swing 架构中常用的观察者模式。
事件队列应该安静一秒钟,以便初始化关机。这是 AWTAutoShutdown 类中的硬编码值。
因此,如果您的摇摆计时器不断生成事件,相隔不到一秒,这将使应用程序无法终止。
看这个例子(下)。它不会终止,因为线程,即使它被标记为守护进程,也会不断向队列中添加事件。如果我们将睡眠时间增加到 1500(1.5 秒)——它会愉快地终止。
public static void main(String[] args)
{
Thread thread = new Thread(new Runnable()
{
@Override
public void run()
{
while (true)
{
// Submit an empty event to the queue
EventQueue.invokeLater(new Runnable()
{
@Override
public void run()
{
}
});
try
{
Thread.sleep(500);
}
catch (InterruptedException e)
{
throw new IllegalStateException(e);
}
}
}
});
thread.setDaemon(true);
thread.start();
}
我们这样做:
private static final class DisplayabilityListener implements HierarchyListener {
private final JComponent component;
private final Timer timer;
private DisplayabilityListener(JComponent component, Timer timer) {
this.component = component;
this.timer = timer;
}
@Override
public void hierarchyChanged(HierarchyEvent e) {
if ((e.getChangeFlags() & HierarchyEvent.DISPLAYABILITY_CHANGED) > 0) {
if (component.isDisplayable()) {
timer.start();
} else {
timer.stop();
}
}
}
}