0

This is a graphics program I made for java and I am having trouble with the stars. The background is already set to black. I want to have multiple stars blinking at the same time instead of one blinking at a time. How would I do that? I have no problem with one star blinking. All I need is multiple at the same time. I do not know what threads are. I used the Thread.sleep() which I got from the internet. If threads would work please tell me that.

Here is the code:

public class graphicwkst9 extends Applet {

public void init()
{

}

public void paint(Graphics g)
{
    g.setColor(Color.BLACK);
    g.fillRect(0,0,500,500);
    while(true){
        g.setColor(Color.WHITE);    
        int x = (int)(Math.random()*495+1);
        int y = (int)(Math.random()*200+1);
        g.fillOval(x,y,5,5);
        try {
            Thread.sleep(500);
        } catch(InterruptedException ex) {
            Thread.currentThread().interrupt();
        }
        g.setColor(Color.BLACK);
        g.fillOval(x,y,5,5);
    }
}

}

2 Answers 2

1

Let's talk about what's wrong...

AWT (and Swing) use a single thread, known as the Event Dispatching Thread (AKA EDT) which is responsible for processing user input (like mouse and keyboard events), system events (like changes to the components internally and externally (coming from the OS)) and, most importantly for this issue, paint requests.

AWT (and Swing) use, what is known as, a passive painting algorithm. That is, painting is done on a request bases, so only those things that need to be changed are changed.

When a paint event is processed by the EDT, the paint method is (eventually) called by the EDT. The expectations is that the paint methods will execute as fast as possible and return. This is important, as any delay in the paint methods will cause delays in how fast program is updated.

So, anything the blocks EDT (like long running looks or Thread.sleep) will stop it from been able to process new events and particular in this context, paint requests.

For more details, take a look at Painting in AWT and Swing for more details...

Now, how to fix it.

Basically, what you need some way to run a background task that makes the updates to what you want to paint and then push those updates to the screen. This ensures that the EDT is not blocked and can continue to process incoming events...

For example...

private int x = 0;
private int y = 0;

private Thread t;
private volatile boolean keepRunning;

@Override
public void init()
{
    setBackground(Color.BLACK); 
}

@Override
public void start() 
{
    keepRunning = true;
    t = new Thread(new Runnable() {
        private boolean state = false;
        public void run() {
            while (keepRunning) {
                if (state) {
                    setForeground(Color.BLACK);
                } else {
                    x = (int)(Math.random()*495+1);
                    y = (int)(Math.random()*200+1);
                    setForeground(Color.WHITE);
                }
                state = !state;
                repaint();
                try {
                    Thread.sleep(500);
                } catch(InterruptedException ex) {
                    keepRunning = false;
                }
            }
        }
    });    
}

@Override
public void stop() {
    keepRunning = false;
}

@Override
public void paint(Graphics g)
{
    super.paint(g);
    g.setColor(getForeground());
    g.fillOval(x,y,5,5);
}

So basically, when started, the Applet creates a Thread that is responsible for making the changes to the applet and finally requests that the Applet be repainted.

Now, the problems...

There are a number of problems with both approaches (yours and mine). The first is the fact the Applets are not double buffered, this means that it can produce flickering when it is painted.

The next is the fact that the values you are painting are being updated by another Thread, this means you could end up with dirty paints as the values are changed while the paint method is updating the UI.

Instead, I would take advantage of the Swing API, as it's components are double buffered by default, making it easier to perform fast updates.

It will also allow you to take advantage of the javax.swing.Timer, which can be configured to schedule regular updates which are executed within the context of the EDT. This means, when making changes the state of the components, they can't be painted, making it safer.

import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JApplet;
import javax.swing.JPanel;
import javax.swing.Timer;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;

public class RandomApplet extends JApplet {

    private RandomPane randomPane;
    private Timer timer;

    public RandomApplet() {
        try {
            UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
        } catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
            ex.printStackTrace();
        }
    }

    @Override
    public void init() {
        EventQueue.invokeLater(new Runnable() {
            @Override
            public void run() {
                setLayout(new BorderLayout());
                randomPane = new RandomPane();
                add(randomPane);
            }
        });
    }

    @Override
    public void start() {
        EventQueue.invokeLater(new Runnable() {
            @Override
            public void run() {
                // Just want to make sure that the timer is not started before the
                // UI is initalised...
                if (timer != null && timer.isRunning()) {
                    timer.stop();
                }
                timer = new Timer(500, new ActionListener() {
                    private boolean state = false;

                    @Override
                    public void actionPerformed(ActionEvent e) {
                        if (state) {
                            randomPane.setForeground(Color.BLACK);
                        } else {
                            randomPane.randomise();
                            randomPane.setForeground(Color.WHITE);
                        }
                        state = !state;
                        repaint();
                    }
                });
                timer.start();
            }
        });
    }

    @Override
    public void stop() {
        timer.stop();
    }

    public class RandomPane extends JPanel {

        private int xPos;
        private int yPos;

        public RandomPane() {
            setBackground(Color.BLACK);
            setForeground(Color.BLACK);
        }

        @Override
        public Dimension getPreferredSize() {
            return new Dimension(495, 200);
        }

        @Override
        protected void paintComponent(Graphics g) {
            super.paintComponent(g);
            Graphics2D g2d = (Graphics2D) g.create();
      g2d.setColor(getForeground());
      g2d.fillOval(xPos, yPos, 5, 5);
            g2d.dispose();
        }

        public void randomise() {
            xPos = (int) (Math.random() * (getWidth() - 5));
            yPos = (int) (Math.random() * (getHeight() - 5));
        }
    }
}

Take a look at Concurrency in Swing for more details...

Sign up to request clarification or add additional context in comments.

Comments

0

I believe that in the code you've provided, the program is creating a star, keeping it there for 500 milliseconds with the Thread delay, and then removing it. Therefore, with each iteration of the loop you're only making one star.

Instead of coding it this way, I'd create a new class entitled "Star" or something of the sort which takes in an X position and a Y position (create set and get methods appropriately), and then generates a number of random stars and put them into a star array to keep track of what you need to display, delay, and then black out again. This can be achieved by using a loop, as it would create them all at once, delay the program, and then black them out all at once.

Your code should look something like this:

public void paint(Graphics g)
{
    g.setColor(Color.BLACK);
    g.fillRect(0,0,500,500);
    while(true){
        g.setColor(Color.WHITE);
        star[] array = new star[numberOfStars (put a number here)];
        for(int i = 0; i < numberOfStars; i++)
        {
            int x = (int)(Math.random()*495+1);
            int y = (int)(Math.random()*200+1);
            array[i] = new star(x, y);
            g.fillOval(x,y,5,5);
        }
        try {
            Thread.sleep(500);
        } catch(InterruptedException ex) {
            Thread.currentThread().interrupt();
        }
        g.setColor(Color.BLACK);
        for(int i = 0; i < numberOfStars; i++)
        {
            int x = array[i].getX();
            int y = array[i].getY();
            g.fillOval(x,y,5,5);
        }
    }
}

4 Comments

Won't this code draw all stars at the same time then black them out again? Would there be a way to do so that some stars go out then others, and then others. If they all went out at once, it would look a bit weird.
"Your code should look like this" - No it shouldn't. You should never use loops or Thread.sleep within the context of the Event Dispatching Thread and especially not within any paint method. This will stop anything form being painted, ever
Chris- yeah, that's what I thought you were asking for. My bad if it's not. MadProgrammer- Could you clarify why you shouldn't? I'm still a novice programmer myself, and so, I've never really had to use the thread delay. I have only used loops in my paint methods before to get multiple images on the screen/frame/applet simultaneously, so I'm confused as to how you would do so otherwise.
The paint method will be called by the Event Dispatching Thread. It is responsible for, amongst other things processing paint requests within the system. If you block (and or sleep) within the EDT, you stop it from processing any events, making it look lie your program has hung (as it can no longer perform paint updates or respond to user events). Check out Concurrency in Swing

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.