views:

46

answers:

2

First of all here are some code snippets:

public void startThread() {
    this.animationThread = new Thread(this);
    this.animationThread.start();
    try {
        this.animationThread.join();
    } catch (InterruptedException e) {
        e.printStackTrace();
    }
}

@Override
public void run() {     
    pirateMainAnimation.animate();
}

public void animate() {
    for (int j = 0; j < 9; j++) {
        try {
            Thread.sleep(250);
        } catch (InterruptedException e) {
            break;
        }
        PirateAnimationPanel.getInstance().setCurrent(j);
        PirateAnimationPanel.getInstance().repaint();
    }
}

I'm trying to animate some images. The thing is that I want the main thread to wait for the animation thread to finish and then to continue. I searched around, read a little bit and decided to use the join() method. It perfectly waits for the thread to finish but I doesn't animate correctly. The repaint() method gets called 2 times instead of nine. I think maybe the problem is because I used singletons. Here is the singleton implementation.

import java.awt.Graphics;
import java.awt.MediaTracker;

import javax.swing.JPanel;

import uk.ac.aber.dcs.piratehangman.animation.PirateMainAnimation;
import uk.ac.aber.dcs.piratehangman.utilityclasses.AnimationThread;

@SuppressWarnings("serial")
public class PirateAnimationPanel extends JPanel {
    private int current;
    private MediaTracker mTracker;
    private PirateMainAnimation pirateMainAnimation;
    private AnimationThread animationThread;

    private PirateAnimationPanel() {
        this.current = 0;
        this.pirateMainAnimation = new PirateMainAnimation();
        mTracker = new MediaTracker(this);
        this.animationThread = new AnimationThread();
        setMediaTracker();
        repaint();
    }

    private void setMediaTracker() {
        for (int i = 0; i < 9; i++) {
            mTracker.addImage(
                    this.pirateMainAnimation.getImagesForAnimation()[i],
                    this.pirateMainAnimation.getImagesForAnimationID()[i]);
            try {
                mTracker.waitForID(this.pirateMainAnimation
                        .getImagesForAnimationID()[i]);
            } catch (InterruptedException e) {
                System.out.println("Error loading image: " + i);
            }
        }
    }

    public void playAnimation() {
        this.animationThread.startThread();
    }

    public void paintComponent(Graphics g) {
        super.paintComponents(g);
        System.out.println("called");
        g.drawImage(this.pirateMainAnimation.getImagesForAnimation()[current],
                0, 0, this);
    }

    private static class PirateAnimationPanelHolder {
        private static final PirateAnimationPanel pirateAnimationPanel =
            new PirateAnimationPanel();
    };

    public static PirateAnimationPanel getInstance() {
        return PirateAnimationPanelHolder.pirateAnimationPanel;
    }

    public void setCurrent(int current) {
        this.current = current;
    }

    public int getCurrent() {
        return current;
    }
}
+2  A: 

I think you mean that the paintComponent() methods only gets called twice. Also I think you should be able to remove the call to super.paintComponents() if you fill the component to the background color.

The repaint() method only marks the component as dirty and requests a re-render on the next paint.

I would have expected the Swing thread to be able to repaint within the 250ms but I'm not sure what other work is being done/rendered. You might want to put a call to MediaTracker.waitForAll() before the animation.

While the static singleton is not adding much I don't think it is causing a problem (in this case).

Update: So the problem is that the join() is on the Swing event Thread which is blocking the repainting of the component. I suggested a call like the following to show the "new game dialog after the last animation:

SwingUtilities.invokeLater(new Runnable() {
    public void run() { showDialog(); }
})
Rob Moore
Thanks for the reply, the interesting thing is that when the call to the join() method is removed from the code. It works fine. Very odd, I'll try your idea though.
gmunk
So the "animation thread" exits/stops after the last call for repaint() which can be before the rendering is finished. Do you allow the application to continue with and without the join? What triggers the playAnimation() is it being called from the Swing thread (mouse/key event or Applet)? Edit: missing word
Rob Moore
playAnimation() is called by a JButton though, it depends on a certain condition. The logic is roughly the following. The user enters a word in a text field and presses the button if the guess is wrong the animation plays. This wrong guessing decrements the users lives, he tries again and if again wrong the animation plays again but this time after it finishesh a pop-up box emerges asking if the user wants a new game. With the join() method in the code this works but the animation renders only the first and the last picture. With the join() method out it renders perfect but the pop up box
gmunk
emerges during the animation... hope it was helpfull
gmunk
Thats the problem. You are holding the Swing Event thread with the join() call. Since you hav thet thread blocked the screen cannot be repainted. You will need to do some other way to Detect that the animation has completed and do any post processing (possibly in the animation thread). What are you trying to do once the animation completes.
Rob Moore
Sorry just finished reading your comment.To popup the dialog I would add it to the end of the animation thread as a: SwingUtilities.invokeLater(new Runnable() { public void run() { showDialog(); } })That will get you back on the Swing thread and show the dialog.
Rob Moore
Well, this thing that I'm building should be a pirate hangman game. Anyway after the second completing of the animation(forgive my awful English) I want the user to decide if he or she wants to play again since the user is dead at the end of the second animation. Cheers for the answer. I'll start working on some other way of detecting if the animation is over.You may want to post this as an answer so I can accept it?
gmunk
+1  A: 

"Note that events being posted to the EventQueue can be coalesced," which may explain the disparity. Also, be certain to build your GUI on event dispatch thread. See A More Complex Image Icon Example for a more detailed discussion.

Addendum: Improving Perceived Performance When Loading Image Icons has a nice SwingWorker example that may simplify the off-loading.

trashgod
I am using the event dispatch thread. Thank you for the reply I'll check the links :).
gmunk
Excellent. @Rob Moore is right about not block the EDT. I've added a link to `SwingWorker` example above.
trashgod