Java 如何同时重复循环

Java 如何同时重复循环,java,graphics,Java,Graphics,这是我为java开发的一个图形程序,我在使用stars时遇到了问题。背景已设置为黑色。我希望有多颗星星同时闪烁,而不是一次闪烁一颗。我该怎么做?我对一颗星闪烁没有问题。我所需要的就是同时多个。我不知道线程是什么。我使用了从互联网上获得的Thread.sleep()。如果线程可以工作,请告诉我 代码如下: 公共类graphicwkst9扩展小程序 { }我相信在您提供的代码中,程序正在创建一个星形,在线程延迟的情况下将其保持500毫秒,然后将其删除。因此,在循环的每次迭代中,您只生成一颗星 我将创

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

代码如下:

公共类graphicwkst9扩展小程序 {


}

我相信在您提供的代码中,程序正在创建一个星形,在线程延迟的情况下将其保持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);
        }
    }
}
public void绘制(图形g)
{
g、 设置颜色(颜色为黑色);
g、 fillRect(0,0500);
while(true){
g、 setColor(Color.WHITE);
star[]数组=新星[numberOfStars(在这里输入一个数字)];
对于(int i=0;i
让我们谈谈问题所在

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

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

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

因此,阻止EDT的任何东西(如长时间运行的外观或
Thread.sleep
)都将阻止它处理新事件,尤其是在这种情况下,绘画请求

有关更多详细信息,请参阅了解更多详细信息

现在,如何修复它

基本上,您需要某种方式来运行后台任务,对要绘制的内容进行更新,然后将这些更新推送到屏幕上。这可确保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
s不是双缓冲的,这意味着它在绘制时会产生闪烁

下一个问题是,您正在绘制的值正在被另一个
线程更新,这意味着当
paint
方法更新UI时,随着值的更改,您可能最终会得到脏的绘制

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

它还允许您利用
javax.swing.Timer
,可以将其配置为计划在EDT上下文中执行的定期更新。这意味着,在更改组件的状态时,不能对其进行绘制,从而使其更安全

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

查看更多详细信息…

此代码是否会同时绘制所有星星,然后再次将它们遮住?有没有一种方法可以让一些星星熄灭,然后是其他星星,然后是其他星星。如果它们同时都消失了,看起来会有点奇怪。“你的代码应该是这样的”-不应该。决不能在事件调度线程的上下文中使用循环或Thread.sleep,尤其是在任何绘制方法中。这将阻止任何形式的绘画,everChris-是的,这就是我认为你要求的。如果不是我的错。MadProgrammer-你能澄清一下为什么你不应该这样做吗?我自己还是一个程序员新手,所以,我从来没有真正使用过线程延迟。我以前只在绘制方法中使用循环来同时在屏幕/框架/小程序上获取多个图像,因此我不知道如果不这样做,将如何进行。绘制方法将由事件调度线程调用。除其他事项外,它还负责处理系统内的喷漆请求。如果您在EDT中阻塞(和或睡眠),您将停止它处理任何事件,使其看起来像您的程序已挂起(因为它无法再执行绘制更新或响应用户事件)。好的,这是有道理的。谢谢你,疯了。
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));
        }
    }
}