简体   繁体   中英

Stopping a Swing timer when a component is hidden

I have a Swing timer ( javax.swing.Timer ) which is used to perform some animation within a custom Swing component.

However, this is causing problems - in particular it seems to stop the application from terminating because of the live timer thread even after all windows are closed. Also it would be nice to avoid the overhead of timers running on hidden objects when the animation cannot be seen.

Ideally I'd like to do the following:

  • Stop the timer when the component is hidden
  • Start the time again whenever the component becomes visible

Is this possible to do (in a thread-safe way of course!)

I am skeptical of your first premise: this simple counter-example shows that a running javax.swing.Timer does not preclude EXIT_ON_CLOSE . The package-private, shared javax.swing.TimerQueue starts a daemon thread, which allows Program Exit . You may be understandably reluctant to rely on this implementation detail, but it may be worth looking for another reason your program fails to exit.

If defer to @kleopatra on AncestorListener ; it should allow you to control the Timer as desired. The duty cycle of a component animation is typical fairly light, and it's usually dominated by rendering; the latter's overhead is small when the component is not visible. It may be worth profiling to verify that the proposed optimization is worth the effort. If so, consider a WindowListener to minimize activity in an inactive or iconified window.

Addendum: A now deleted answer suggested overriding setVisible() to condition the timer. While superficially appealing, the approach is brittle and scales poorly. The listener approach leverages the observer pattern commonly used in Swing architecture .

The event queue should be quiet for one second for the shutdown to be initialized. That is a hard-coded value in AWTAutoShutdown class.

So if your swing timer keeps generating events, less then a second apart, that would keep the application from terminate.

Look at this example (below). It would not terminate, because the thread, even though it marked as deamon, keeps adding events to the queue. If we increase the sleep to 1500 (1.5 seconds) - it would terminate happily.

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

We do it like this:

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

}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM