如何同时重复循环

时间:2013-12-05 00:54:38

标签: java graphics

这是我为java制作的图形程序,我遇到了星星问题。背景已设置为黑色。我希望同时闪烁多颗星星,而不是一次闪烁一颗。我该怎么办?一颗星闪烁我没问题。我所需要的只是同时多个。我不知道线程是什么。我使用了从互联网上获得的Thread.sleep()。如果线程有效,请告诉我。

以下是代码:

public class graphicwkst9扩展了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 个答案:

答案 0 :(得分:1)

让我们谈谈什么是错的......

AWT(和Swing)使用单个线程,称为事件调度线程(AKA EDT),负责处理用户输入(如鼠标和键盘事件),系统事件(如内部和外部组件的更改)(来自操作系统)),最重要的是,这个问题,绘制请求。

AWT(和Swing)使用,即所谓的被动绘画算法。也就是说,绘画是在请求的基础上完成的,所以只有那些需要改变的东西才会被改变。

当EDT处理绘制事件时,EDT会(最终)调用paint方法。期望是paint方法将尽可能快地执行并返回。这很重要,因为paint方法中的任何延迟都会导致程序更新速度的延迟。

因此,任何块EDT(如长时间运行或Thread.sleep)都会阻止它处理新事件,特别是在此上下文中,绘制请求。

有关详细信息,请查看Painting in AWT and Swing了解详情......

现在,如何解决它。

基本上,您需要某种方式来运行后台任务,该任务会对您想要绘制的内容进行更新,然后将这些更新推送到屏幕上。这样可以确保EDT不会被阻止,并且可以继续处理传入的事件......

例如......

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

基本上,在启动时,Applet会创建一个Thread,负责对applet进行更改,最后请求重新绘制Applet

现在,问题......

这两种方法都存在许多问题(你和我的)。第一个问题是Applet s不是双缓冲的,这意味着它在绘制时会产生闪烁。

接下来的事实是您正在绘制的值正由另一个Thread更新,这意味着当paint方法更新时,您可能会更改脏涂料UI。

相反,我会利用Swing API,因为默认情况下它的组件是双缓冲的,这使得更容易执行快速更新。

它还允许您利用javax.swing.Timerimport 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)); } } } 可以配置为安排在EDT上下文中执行的定期更新。这意味着,当改变组件的状态时,它们不能被涂漆,使其更安全。

{{1}}

请查看Concurrency in Swing了解更多详情......

答案 1 :(得分:0)

我相信在您提供的代码中,程序正在创建一个星形,使用Thread延迟将其保持500毫秒,然后将其删除。因此,对于循环的每次迭代,您只能制作一颗星。

不是用这种方式编码,而是创建一个名为“Star”的新类或者某种类型的东西,它接受X位置和Y位置(创建set和get方法),然后生成一个数字随机星星并将它们放入星形阵列以跟踪您需要显示的内容,延迟,然后再次黑屏。这可以通过使用循环来实现,因为它会立即创建所有循环,延迟程序,然后一次将它们全部黑掉。

您的代码应如下所示:

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