-1

This is my first time trying to use another thread in java, could someone tell me how to make it work please? I've read others topics about it, but I didn't find a solution.

I'd like to draw a gif in another thread (drawn at at a random position and during the duration of its animation).

The problem is that drawImage() in the second thread just doesn't do anything. My counter works well (it prints 1.. 2.. 3 ...), but no image is drawn (or I can't see it).

The condition is false at the begining, then it is true at one moment (to create only one new thread and no more), then it is false again.

if (condition) {
    (new ThreadGif(this,g)).start();
}

However when I remove the condition in paintComponent(), it draws something which means that drawImage() works. So when it creates lots of new threads, every image of the gif is drawn at a random location and it starts the gif again and again (and the counter(s) still work well).

This could be fine, but I don't think creating thousands of new thread is the answer : I just need one. And also, I need just one random position for each gif, not one different for each image of the gif.

I hope I've been clear enough. Please help me understand how to make it work :) Thank you very much.

Here are simplified versions of my two classes :

ThreadGif.java :

public class ThreadGif extends Thread { 

    Screen screen;
    Graphics g;
    boolean running = true;

    public ThreadGif(Screen screen, Graphics g) {
        this.g = g;
        this.screen = screen;
    }

    public void run() {

        int aleaX = new Random().nextInt(300)/100;
        int aleaY = new Random().nextInt(300)/100;
        int compt = 1;

        while (running) {
            g.drawImage(new ImageIcon("res/feu.gif").getImage(), screen.tailleCase*aleaX, screen.tailleCase*aleaY, screen.tailleCase*2, screen.tailleCase*2, screen);
            System.out.println("thread " + compt);
            compt++;
            try {
                Thread.sleep(sleepTime);
            } catch(InterruptedException e) {
                e.printStackTrace ();
            }
        }
    }       
}

Screen.java :

public class Screen extends JPanel implements Runnable {
    Thread thread = new Thread(this);

    public Screen(Frame frame) {
        this.frame = frame;
        thread.start();
    }

    public void paintComponent(Graphics g) {
        g.clearRect(0, 0, this.frame.getWidth(), this.frame.getHeight());
        if (condition) {
            (new ThreadGif(this,g)).start();
        }
    }

    public void run() { 
        while (running) {
            repaint();
            try {
                Thread.sleep(sleepTime);
            } catch(InterruptedException e) {
                e.printStackTrace ();
            }
        }
        System.exit(0);
    }
}
Hovercraft Full Of Eels
  • 283,665
  • 25
  • 256
  • 373
Dreamk33
  • 245
  • 3
  • 13
  • 1
    Painting with the paintComponent Graphics parameter off of the Swing event thread smells like an invitation to threading disaster, doesn't it? Shouldn't it be the other way around? -- Create a `SwingWorker` that has its own inner Timer, create your images in the SwingWorker, export the images to the GUI when done via the publish/process method pair, and then call repaint? – Hovercraft Full Of Eels Feb 24 '16 at 19:28
  • 1
    Also this: `g.drawImage(new ImageIcon("res/feu.gif").getImage(), ....);` is broken. Why keep re-reading in the same image over and over again, when it is much simpler and economical to read it in just once and store it into a variable? – Hovercraft Full Of Eels Feb 24 '16 at 19:46
  • I'll search about SwingWork thank you. Yes I definitely agree, i am changing it. – Dreamk33 Feb 24 '16 at 19:51

1 Answers1

1

Pretty simple: you mixed two approaches up.
The paintComponent-method launches a new ThreadGif every time it's called and ThreadGif itself paints within it's thread until it's terminated, but without refreshing the screen.

These two approaches combined might either result in strange behaviour, e.g. two images painted where pieces overlay each other, or the new ThreadGif simply renders the new image every-time you repaint the screen.

Solution: Start by assigning each class specific tasks, without mixing anything up, or splitting tasks between two classes. E.g.:

  • ThreadGif doesn't paint anything itself, but repaints the Screen. The Screen can request the image that should be displayed from ThreadGif.
  • Make ThreadGif an own Component that handles it's own rendering and ommit the Screen-class from painting anything.
  • Painting with the paintComponent Graphics parameter off of the Swing event thread smells like an invitation to threading disaster, doesn't it? Shouldn't it be the other way around? -- Create a `SwingWorker` that has its own inner Timer, create your images in the SwingWorker, export the images to the GUI when done via the publish/process method pair, and then call repaint? – Hovercraft Full Of Eels Feb 24 '16 at 19:26
  • @HovercraftFullOfEels Well, **how/when** the screen refreshed is another point. `SwingWorker` would be an option, but that's up to OP I'd say. –  Feb 24 '16 at 19:32
  • Thank you for your answer :) The paintComponent only start a new ThreadGif if the condition is true, which happens just once. I tried without just to see if it would draw something but I knew it was a bad idea. Indeed, what you describe has to be what happens without the condition. But why doesn't it work when I only create one new thread? For you solutions : 1st one: I created the class ThreadGif because it was too hard to paint the gif within Screen.paintComponent(). It has no other use. What do you mean by "The screen can request the image..."? Can you say more about it? 2nd one: – Dreamk33 Feb 24 '16 at 19:33
  • 2nd one: I don't know what to do with it sorry I don't really konw what a Component is, I need to look into it. I'm a beginner. – Dreamk33 Feb 24 '16 at 19:36
  • @Dreamk33 By "The screen can request the image..." I just meant you could add a getter to the `ThreadGif`-class that returns the image that should be painted at the moment the getter is called. As for the second one: Basically I meant you could merge the `ThreadGif` and `Screen`-class, since they apparently have a 1:1-relation. So make `Screen` launch it's own thread that updates the images instead of leaving this part to a separate class. Though in general: why do you even need a Thread? You're code will only paint the same image over and over. –  Feb 24 '16 at 19:40
  • Yes I could merge them, not sure about it but I'll try. I'm going to add a timer so that the image stop being drawn when the animation is finished. I couldn't make it work, so I thought drawing each gif in a new thread was the solution. – Dreamk33 Feb 24 '16 at 19:48
  • @Dreamk33 If the gif is a sequence of images, you'd be a lot better off simply using the classes provided by the Java-API for a variety of reasons. E.g. your code will always create a new `ImageIcon`, so the call to `getImage()` will always result in the same image (the first of the sequence). –  Feb 24 '16 at 20:00
  • No, animated gifs work fine with drawImage(), it wasn't the problem ;) Finally it works with getters for the random positions. Thank you very much :) – Dreamk33 Feb 24 '16 at 20:07
  • @Dreamk33 glad to help you :) –  Feb 24 '16 at 20:11