2

I am writing a game using Java Swing. I want to paint each time a loop executes with a small delay in between to create a cascade effect on screen. I believe that the efficiency routines in the system are collapsing the calls to repaint() into a single call. At any rate, the changes all occur at once after the total delay. Is there some way to force the system to repaint immediately and then delay on each iteration of the loop?

My Code:

for(int i=0;i<10;i++){
    JButton[i].setBackground(Color.WHITE);
    JButton[i].repaint();
    Thread.sleep(2000);
    JButton[i].setBackground(Color.GRAY);
    JButton[i].repaint();
}
Bill the Lizard
  • 398,270
  • 210
  • 566
  • 880
Eternal Noob
  • 2,717
  • 5
  • 27
  • 41

2 Answers2

7

You can use JComponent.paintImmediately to force an immediate repaint.

EDIT: After reading your question again it occurs to me that you might be executing your logic on the event dispatch thread. This would mean that the repaint requests will not be executed until after your method returns. If you put your code into another thread then that will probably fix the problem and it will be a lot nicer than using paintImmediately.

void uiFunction() {
    new Thread() {
        public void run() {
            for(int i = 0; i < 10; i++) {
                final JButton b = buttons[i];
                SwingUtilities.invokeLater(new Runnable() {
                    b.setBackground(Color.WHITE);
                    b.repaint();
                }
                Thread.sleep(2000);
                SwingUtilities.invokeLater(new Runnable() {
                    b.setBackground(Color.GRAY);
                    b.repaint();
                }
            }
        }
    }.run();
}

It's a bit mucky but hopefully it gives you an idea of where to begin.

Cameron Skinner
  • 51,692
  • 2
  • 65
  • 86
  • Oh, you have to call that from the EDT. -1. – Chris Dennett Nov 08 '10 at 01:07
  • Yes, you do. Not sure why you think that's worth a downvote. Is it because I downvoted your answer? – Cameron Skinner Nov 08 '10 at 01:11
  • Er...yes it did. Since it seems that the original problem was that everything was running on the event dispatch thread. Thanks for removing the downvote. – Cameron Skinner Nov 08 '10 at 01:17
  • Thank you all for comments...As Derek suggested it was a thread issue... I created a new thread and it works fine... Thanks Cameron for code for creating new thread. @Cameron, it works for me without using invokeLater() method, I know why you are using it there...Good solution... – Eternal Noob Nov 08 '10 at 18:11
  • Remember that all GUI operations must be executed on the event dispatch thread: this includes the `setBackground` calls. That's why `invokeLater` is used (you could also use `invokeAndWait`). – Cameron Skinner Nov 09 '10 at 01:33
  • can anyone explain why the repaint events will not happen until after the method returns? – john k Jul 11 '18 at 04:14
2

Not sure if I'm on the right track here, but is this a thread issue? Because the sleep will be putting the current thread to sleep. So if the repaint is on the current thread, you will be effectively queuing a repaint, holding, then queuing another repaint. Then the method ends and the event processing loop of the graphics thread fires, executing two repaints (collapsed) making everything happen at once.

Based on that thought I think you might have two options:

  1. Put the code on a second thread so that it doesn't halt the main graphics thread and also because it means that the graphics thread cando a repaint along side the sleep happening.

  2. Before you sleep (not really recommended I would think), trigger the event loop to force the repaint to happen. I'd have to look this up. Cannot remember how to do it off the top of my head. :-)

I'd probably be looking at #1 as the best option.

drekka
  • 20,957
  • 14
  • 79
  • 135