Swing计时器用于显示网格。

5

我有一种感觉,我不知道swing Timer如何工作。 我还是Java GUI API的新手,我正在编写的程序仅用于测试自己并帮助我更熟悉其内部工作。

它的作用是等待用户按下“开始”按钮,然后迭代显示(一个白色或黑色JPanel的网格),在1秒间隔内显示简单的元胞自动机模拟,并在按下暂停按钮时暂停(与开始按钮相同,但更改名称)。 网格中的每个单元格都应以随机颜色(白色/黑色)开始。 它实际上是暂停了大约半秒钟,然后“运行”另外半秒钟,然后暂停,然后再运行,如此往复。

import java.awt.*;
import java.awt.event.*;
import javax.swing.*;

public class CA_Driver extends JFrame{

    private JPanel gridPanel, buttonPanel;
    private JButton start_pause, pause;

    private static Timer timer;
    private Color black = Color.black;
    private Color white = Color.white;
    static Color[][] currentGrid, newGrid;
    static Cell[][] cellGrid;
    static boolean run, stop;
    static int height = 20, width = 30, state;

    public CA_Driver(){
        stop = false;
        run = false;
        currentGrid = new Color[height][width];
        newGrid = new Color[height][width];
        cellGrid = new Cell[height][width];

        //Initialize grid values
        for (int x = 0; x < currentGrid.length; x++)
            for (int y = 0; y < currentGrid[x].length; y++){
                int z = (int) (Math.random() * 2);
                if (z == 0)
                    currentGrid[x][y] = newGrid[x][y] = white;
                else currentGrid[x][y] = newGrid[x][y] = black;
            }
        //Create grid panel
        gridPanel = new JPanel();
        gridPanel.setLayout(new GridLayout(height,width));
        //Populate grid 
        for (int x = 0; x < newGrid.length; x++)
            for (int y = 0; y < newGrid[x].length; y++){
                cellGrid[x][y] = new Cell(x,y);
                cellGrid[x][y].setBackground(newGrid[x][y]);
                int z = (int) Math.random();
                if (z == 0) cellGrid[x][y].setBackground(black);
                else cellGrid[x][y].setBackground(currentGrid[x][y]);
                gridPanel.add(cellGrid[x][y]);
            }

        //Create buttons
        state = 0;
        start_pause = new JButton();
        start_pause.setText("Start");
        start_pause.addActionListener(new ActionListener(){
            public void actionPerformed(ActionEvent arg0) {
                if (state == 0) {
                    start_pause.setText("Pause");
                    run = true;
                    timer.start();
                    state += 1;
                }

                else {
                    start_pause.setText("Start");
                    run = false;
                    timer.stop();
                    state -= 1;
                }
            }
        });



        buttonPanel = new JPanel(new BorderLayout());
        buttonPanel.add(start_pause, BorderLayout.NORTH);
//      buttonPanel.add(pause, BorderLayout.EAST);

        //Initialize and display frame
        this.add(gridPanel, BorderLayout.NORTH);
        this.add(buttonPanel, BorderLayout.SOUTH);
        this.setDefaultCloseOperation(EXIT_ON_CLOSE);
        //this.setSize(500, 500);
        pack();
        this.setVisible(true);

        //Initialize timer
        timer = new Timer(1000, new ActionListener(){
            public void actionPerformed(ActionEvent arg0) {


                for (int x = 0; x < cellGrid.length; x++)
                    for (int y = 0; y < cellGrid[x].length; y++){
                        cellGrid[x][y].setColor();
                        currentGrid[x][y] = newGrid[x][y];
                    }
                //Display processing for next frame
                for (int x = 0; x < currentGrid.length; x++)
                    for (int y = 0; y < currentGrid[x].length; y++){
                        int b = checkNeighbors(y,x);
                        if (b > 4 || b < 2)
                            newGrid[x][y] = black;
                        else newGrid[x][y] = white;
                    }
                if(!run) timer.stop();
            }
        });
    }

    public static void main(String[] args) {
        new CA_Driver();
    }


    private int checkNeighbors(int w, int h){
        int b = 0;
        //Top Left
        if((w != 0) && (h != 0) && (currentGrid[h - 1][w - 1] == black))
            b++;
        //Top Middle
        if((h != 0) && (currentGrid[h - 1][w] == black))
            b++;
        //Top Right
        if((w != width - 1) && (h != 0) && (currentGrid[h - 1][w + 1] == black))
            b++;
        //Middle Left
        if((w != 0) && (currentGrid[h][w - 1] == black))
            b++;
        //Middle Right
        if((w != width - 1) && (currentGrid[h][w + 1] == black))
            b++;
        //Bottom left
        if((w != 0) && (h != height - 1) && (currentGrid[h + 1][w - 1] == black))
            b++;
        //Bottom Middle
        if((h != height - 1) && (currentGrid[h + 1][w] == black))
            b++;
        //Bottom Right
        if((w != width - 1) && (h != height - 1) && (currentGrid[h + 1][w + 1] == black))
            b++;
        return b;
    }

    private class Cell extends JPanel{
        private Color c;
        private int posx, posy;

        public Cell(int x, int y){
            posx = x;
            posy = y;
        }

        public Point getLocation(){
            return new Point(posx, posy);
        }

        public void setColor(){
            c = newGrid[posx][posy];
            setBackground(c);
        }

        public Dimension getPreferredSize(){
            return new Dimension(10,10);
        }
    }


}

这是计时器部分:
timer = new Timer(1000, new ActionListener(){
    public void actionPerformed(ActionEvent arg0) {


        for (int x = 0; x < cellGrid.length; x++)
            for (int y = 0; y < cellGrid[x].length; y++){
                cellGrid[x][y].setColor();
                currentGrid[x][y] = newGrid[x][y];
            }
        //Display processing for next frame
        for (int x = 0; x < currentGrid.length; x++)
            for (int y = 0; y < currentGrid[x].length; y++){
                int b = checkNeighbors(y,x);
                if (b > 4 || b < 2)
                    newGrid[x][y] = black;
                else newGrid[x][y] = white;
            }
        if(!run) timer.stop();
    }
    });

我计划稍后添加更多功能,以便用户可以对各种变量(如网格大小和迭代速度)进行更多控制,但我想先让显示的核心功能正常工作。我相当确定问题出在我如何使用Timer类上,因为时间不准确。
我的第一个问题是:我是否正确使用了Timer类?如果是这样,那么问题出在哪里?如果不是,我应该如何使用它?
更新 这是个好主意,MadProgrammer,并且很高兴知道我正在正确使用Timer。我意识到它“运行”的部分实际上是每个单元格更新其颜色所需的时间,因此现在我的程序非常缓慢和低效。
这是我提高速度和效率的想法。主要是,我将使用计时器延迟来处理下一次迭代的输出,然后下一次计时器“触发”时,我会更改一个“tick”变量,每个单元格都会将其用作更改颜色的信号,如建议所述。为了实现这一点,我已经向每个单元格添加了一个计时器(这是一个好/坏的主意吗?),它会等待一段时间,然后在阻塞的while循环中等待看到内部的“tick”等于全局“tick”,并在发生这种情况时立即更改颜色。
最终结果是它一启动就会冻结。
这是我添加到Cell类构造函数的计时器:
c_timer = new Timer(500, new ActionListener(){
    public void actionPerformed(ActionEvent e){
        c_timer.stop();
        while (c_tick != tick);
        setBackground(currentGrid[posx][posy]);
        c_tick = 1 - c_tick;
        if(run) timer.restart();
    }
});
        c_timer.start();

我修改了全局计时器的方法如下:

timer = new Timer(1000, new ActionListener(){
    public void actionPerformed(ActionEvent arg0) {
        for (int y = 0; y < height; y++)
            for (int x = 0; x < width; x++)
                currentGrid[y][x] = newGrid[y][x];
        tick = 1 - tick;

        for (int y = 0; y < height; y++)
            for (int x = 0; x < width; x++){
                if (b[y][x] > 6 || b[y][x] < 1) newGrid[y][x] = white;
                else newGrid[y][x] = black;
            }

        for (int y = 0; y < height; y++)
            for (int x = 0; x < width; x++)
                b[y][x] = checkNeighbors(x,y);
        if(!run) timer.stop();
    }
});

除了这些更改,我还删除了Cell类中的setColor()方法。有人能指出我的错误吗?
更新2: 我应该早点更新,但简单来说,我发现这完全是错误的做法。而不是制作一个充满组件并更改它们背景的面板,你应该使用网格来绘制面板。
@Override
public void paintComponent(Graphics g){
    super.paintComponent(g);
    for (int h = 0; h < board_size.height; h++){
        for (int w = 0; w < board_size.width; w++){
            try{
                if (grid[h][w] == BLACK)
                    g.setColor(BLACK);
                else g.setColor(WHITE);
                g.fillRect(h * cell_size, w * cell_size, cell_size, cell_size);
            } catch (ConcurrentModificationException cme){}
        }
    }
}

在每个计时器“tick”上,您首先重新绘制网格,然后处理下一个要在下一个tick上绘制的迭代。这样更有效率,并且可以立即更新。
我使用了一个修改过的JPanel作为主网格组件,它实现了一个ActionListener来处理用户在GUI的其余部分执行的每个操作以及每个计时器tick。
public void actionPerformed(ActionEvent e) {

        //Timer tick processing: count surrounding black cells, define next iteration
            //using current rule set, update master grid 
        if (e.getSource().equals(timer)){
        //Processing for each tick
        }

        else if(e.getSource()...
        //Process events dispached by other components in gui
}

当然,你需要将面板设置为计时器的操作监听器。

3
一切似乎都还好。就个人而言,我会更新状态,然后在每个周期应用它。你可以考虑在setColor方法中添加调用repaint方法。 - MadProgrammer
5
我不明白为什么这个帖子会被踩。它包含了一个 MCVE(最小化完整可复现示例)和一个明确的问题。 +1 - Andrew Thompson
1
这个问题只是一个“如何优化我的代码”问题吗?也许我没有读对。 - Paul Samsotha
1
为了实现这个,我在每个单元格中添加了一个计时器。这是个坏主意。每个Timer实例都是一个新的Thread。而且我无法理解你的问题是什么? - SeniorJD
“我也不知道你的问题是什么?” - 我也是。我真的很想在这里提供帮助(并获得奖励 - 是的,我是一个声望猎人),但问题的更新使得很难确定一个可接受的答案应该是什么样子的... - Marco13
虽然你的方法听起来可行,但更常见的方法是有一个update()函数,使“世界数据结构”变为“当前”。在你的情况下,数据结构是CA,“current”是细胞的下一个配置。像你展示的那样,paint函数只是绘制数据结构,即网格。现在,计时器函数只需调用update(),然后调用repaint()。这是一种非常简单和模块化的动画思考方式。 - Gene
3个回答

2
在问题的第一部分中,您对Timer类的使用看起来确实是正确的。在java.swing.Timer中发生的情况是,ActionListener会在特定间隔(由延迟参数指定)上的事件分派线程上触发。
这也意味着您放置在ActionListener中的代码应该快速执行。当您的ActionListener代码正在执行时,UI无法更新,因为UI线程(事件分派线程)正在执行ActionListener代码。这在该类的javadoc文档中有明确说明。

尽管所有计时器都使用单个共享线程(由执行的第一个计时器对象创建)进行等待,但计时器的操作事件处理程序在另一个线程上执行--事件分派线程。这意味着计时器的操作处理程序可以安全地对Swing组件执行操作。但是,这也意味着处理程序必须快速执行,以保持GUI响应。

这正是您在第一个更新中遇到的情况。
new Timer(500, new ActionListener(){
  public void actionPerformed(ActionEvent e){
    //...
    while (c_tick != tick){}
    //...
  }
});

在这里使用while循环会阻塞事件调度线程。c_tick != tick检查永远不会改变,因为所涉及的变量只在EDT上进行调整,而你正在用循环阻塞它。
你的第二次更新似乎表明通过从面板切换一切都正常工作了。然而有两个奇怪的问题:
1. catch ConcurrentModificationException cme代码块。在你发布的代码中,我不能立即发现你会遇到ConcurrentModificationException的地方。请记住,Swing是单线程的。所有可能与Swing组件交互的操作都应在EDT上执行,使得遇到ConcurrentModificationException的机会比多线程应用程序要小得多。 2. 你说过,“当然,你必须将面板设置为定时器的操作侦听器”。这似乎是不正确的。无论哪个ActionListener附加到Timer上,都需要交换当前网格和下一个网格,并计算下一个网格。一旦计算出下一个网格,就需要安排重新绘制网格面板。无论是匿名/内部/独立类还是网格面板本身作为监听器都是无关紧要的(至少在功能上是这样,设计上我不会选择让网格面板成为监听器)。
另外提醒:当你需要交换当前和新网格时,请使用以下代码。
for (int y = 0; y < height; y++){
  for (int x = 0; x < width; x++){
    currentGrid[y][x] = newGrid[y][x];
  }
}

如果您仍然遇到性能问题,可以尝试使用System.arrayCopy,它比手动循环数组可能要快得多。

0

这是一个生命游戏的示例,以传统的Java Swing方式每半秒更新屏幕。

添加控制设置网格大小和更新速率的控件也很简单,还可以添加一个编辑模式,在动画停止时可以用鼠标设置单元格。调用lifePane.run(newUpdateInterval)更改更新速率,或调用lifePane.run(0)暂停。调用lifePane.setGenSize(width, height)更改网格大小。

使用单独的线程进行生成计算(正如所建议的,但我在此没有实现)的主要价值在于当您操作GUI时动画将继续播放。例如,如果您使用滑块来控制速度,则如果在UI线程中计算生成次数,动画将暂停。

补充为了增加趣味性,我添加了控件并使用了java.utils.timer而不是Swing计时器来获得在this Gist中呈现的额外线程效果。

但是,如果您不介意在操作“鼠标按下”GUI项目时暂停,那么单线程就可以了。我旧的笔记本电脑在Swing事件线程中以20次更新每秒的速度运行1000x1000的一代大小,并且GUI仍然表现得非常好。

方法update()从当前生成填充下一代,然后交换缓冲区。 paintComponent的重写只绘制当前生成。通过这种组合,计时器所需做的就是updaterepaint

其他可能对您有用的约定是处理窗口调整大小和组织相邻计算的方法。了解良好的惯用语可以避免冗长的代码。

import java.awt.*;
import java.awt.event.*;
import java.util.Random;
import javax.swing.*;

public class Life {

    protected LifePane lifePane;

    public static class LifePane extends JComponent {

        private int rows, cols;
        private byte[][] thisGen, nextGen;
        private Timer timer;

        public LifePane(int rows, int cols) {
            setGenSize(rows, cols);
        }

        public final void setGenSize(int rows, int cols) {
            this.rows = rows;
            this.cols = cols;
            thisGen = new byte[rows][cols];
            nextGen = new byte[rows][cols];
            Random gen = new Random();
            for (int i = 0; i < rows; i++) {
                for (int j = 0; j < cols; j++) {
                    thisGen[i][j] = toByte(gen.nextBoolean());
                }
            }
        }

        @Override
        protected void paintComponent(Graphics g) {
            int width = getWidth();
            int height = getHeight();
            // Clear the background.
            g.setColor(Color.WHITE);
            g.fillRect(0, 0, width, height);
            // Set the 1-valued cells black.
            g.setColor(Color.BLACK);
            int y0 = 0;
            for (int i = 1; i < rows; i++) {
                int y1 = i * height / (rows - 1);
                int x0 = 0;
                for (int j = 1; j < cols; j++) {
                    int x1 = j * width / (cols - 1);
                    if (thisGen[i][j] != 0) {
                        g.fillRect(x0, y0, x1 - x0, y1 - y0);
                    }
                    x0 = x1;
                }
                y0 = y1;
            }
        }

        /**
         * Make the next generation current.
         */
        private void swapGens() {
            byte [][] tmp = thisGen;
            thisGen = nextGen;
            nextGen = tmp;
        }

        private static byte toByte(boolean booleanVal) {
            return booleanVal ? (byte) 1 : (byte) 0;
        }

        // Implementation of Conway's Game of Life rules.
        private void updateCell(int x0, int x, int x1, int y0, int y, int y1) {
            int n = thisGen[y0][x0] + thisGen[y0][x] + thisGen[y0][x1] +
                    thisGen[y] [x0] +                  thisGen[y] [x1] +
                    thisGen[y1][x0] + thisGen[y1][x] + thisGen[y1][x1];
            nextGen[y][x] = 
                (thisGen[y][x] == 0) ? toByte(n == 3) : toByte(n >> 1 == 1);
        }

        private void updateRow(int y0, int y, int y1) {
            updateCell(cols - 1, 0, 1, y0, y, y1);
            for (int j = 1; j < cols - 1; ++j) {
                updateCell(j - 1, j, j + 1, y0, y, y1);
            }
            updateCell(cols - 2, cols - 1, 0, y0, y, y1);
        }

        // Update the grid as a toroid and swap buffers.
        public void update() {
            updateRow(rows - 1, 0, 1);
            for (int i = 1; i < rows - 1; i++) {
                updateRow(i - 1, i, i + 1);
            }
            updateRow(rows - 2, rows - 1, 0);
            swapGens();
        }

        /**
         * Run the life instance with given update interval.
         * 
         * @param updateInterval interval in milliseconds, <= 0 to stop
         * @return this
         */
        public LifePane run(int updateInterval) {
            if (timer != null) {
                timer.stop();
                timer = null;
            }
            if (updateInterval > 0) {
                timer = new Timer(updateInterval, new ActionListener() {
                    @Override
                    public void actionPerformed(ActionEvent e) {
                        update();
                        repaint();
                    }
                });
                timer.start();
            }
            return this;
        }
    }

    public void run(int width, int height, int updateInterval) {
        JFrame frame = new JFrame("Life");
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.setLocationByPlatform(true);
        lifePane = new LifePane(width, height).run(updateInterval);
        frame.setContentPane(lifePane);
        frame.setPreferredSize(new Dimension(1024, 800));
        frame.pack();
        frame.setVisible(true);
    }

    public static void main(String[] args) {
        SwingUtilities.invokeLater(new Runnable() {
            @Override
            public void run() {
                new Life().run(100, 100, 500);
            }
        });
    }
}

-1
我计划稍后添加更多功能,以便用户可以对各种变量(如网格大小和迭代速度)进行更多控制,但我想先让显示的核心功能正常工作。我相当确定问题在于我如何使用Timer类,因为时间是有问题的。
这是一个很好的策略,程序运行良好,但它可能更高效和可扩展。
例如,我建议使用自定义SwingWorker类来执行计算,然后将消息发送回UI。
以下是我如何在SwingWorker中创建此示例。 此外,Oracle资源站点提供了其他信息:http://docs.oracle.com/javase/tutorial/uiswing/concurrency/simple.html
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.GridLayout;
import java.awt.Point;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;

import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.SwingWorker;

public class CA_Driver extends JFrame
{
    private JPanel gridPanel, buttonPanel;
    private JButton start_pause, pause;
//  private static Timer timer;
    private Color black = Color.black;
    private Color white = Color.white;
    static Color[][] currentGrid, newGrid;
    static Cell[][] cellGrid;
    static boolean stop;
    static int height = 20, width = 30, state;

    boolean run;

    private synchronized boolean getRun()
    {
        return run;
    }

    private synchronized void setRun(boolean run)
    {
        this.run = run;
    }

    /**
     * http://docs.oracle.com/javase/tutorial/uiswing/concurrency/simple.html
     *
     */
    SwingWorker worker = createNewWorker();

    private SwingWorker createNewWorker()
    {
        return
            new SwingWorker<Void, Void>()
            {
                protected Void doInBackground() throws Exception
                {
                    while(getRun())
                    {
                        for (int x = 0; x < cellGrid.length; x++)
                        {
                            for (int y = 0; y < cellGrid[x].length; y++)
                            {
                                cellGrid[x][y].setColor();
                                currentGrid[x][y] = newGrid[x][y];
                            }
                        }

                        //Display processing for next frame
                        for (int x = 0; x < currentGrid.length; x++)
                        {
                            for (int y = 0; y < currentGrid[x].length; y++)
                            {
                                int b = checkNeighbors(y,x);

                                if (b > 4 || b < 2)
                                {
                                    newGrid[x][y] = black;
                                }
                                else
                                {
                                    newGrid[x][y] = white;
                                }
                            }
                        }

                        try
                        {
                            Thread.sleep(1000);
                        }
                        catch(InterruptedException e)
                        {
                            e.printStackTrace();
                        }
                    }

                    return null;
                }

                @Override
                protected void done()
                {
                    super.done();
                }
            };
    }

    public CA_Driver()
    {
        stop = false;
        setRun(false);
        currentGrid = new Color[height][width];
        newGrid = new Color[height][width];
        cellGrid = new Cell[height][width];
        //Initialize grid values
        for(int x = 0 ; x < currentGrid.length ; x++)
            for(int y = 0 ; y < currentGrid[x].length ; y++)
            {
                int z = (int) (Math.random() * 2);
                if(z == 0)
                    currentGrid[x][y] = newGrid[x][y] = white;
                else
                    currentGrid[x][y] = newGrid[x][y] = black;
            }
        //Create grid panel
        gridPanel = new JPanel();
        gridPanel.setLayout(new GridLayout(height, width));
        //Populate grid 
        for(int x = 0 ; x < newGrid.length ; x++)
            for(int y = 0 ; y < newGrid[x].length ; y++)
            {
                cellGrid[x][y] = new Cell(x, y);
                cellGrid[x][y].setBackground(newGrid[x][y]);
                int z = (int) Math.random();
                if(z == 0)
                    cellGrid[x][y].setBackground(black);
                else
                    cellGrid[x][y].setBackground(currentGrid[x][y]);
                gridPanel.add(cellGrid[x][y]);
            }
        //Create buttons
        state = 0;
        start_pause = new JButton();
        start_pause.setText("Start");
        start_pause.addActionListener(new ActionListener()
        {
            public void actionPerformed(ActionEvent arg0)
            {
                if(state == 0)
                {
                    start_pause.setText("Pause");

                    setRun(true);
                    worker = createNewWorker();
                    worker.execute();

//                  timer.start();

                    state += 1;
                }
                else
                {
                    start_pause.setText("Start");

                    setRun(false);

//                  timer.stop();

                    state -= 1;
                }
            }
        });
        buttonPanel = new JPanel(new BorderLayout());
        buttonPanel.add(start_pause, BorderLayout.NORTH);
        //      buttonPanel.add(pause, BorderLayout.EAST);
        //Initialize and display frame
        this.add(gridPanel, BorderLayout.NORTH);
        this.add(buttonPanel, BorderLayout.SOUTH);
        this.setDefaultCloseOperation(EXIT_ON_CLOSE);
        //this.setSize(500, 500);
        pack();
        this.setVisible(true);

        worker.execute();

        /*
        //Initialize timer
        timer = new Timer(1000, new ActionListener()
        {
            public void actionPerformed(ActionEvent arg0)
            {
                for(int x = 0 ; x < cellGrid.length ; x++)
                    for(int y = 0 ; y < cellGrid[x].length ; y++)
                    {
                        cellGrid[x][y].setColor();
                        currentGrid[x][y] = newGrid[x][y];
                    }
                //Display processing for next frame
                for(int x = 0 ; x < currentGrid.length ; x++)
                    for(int y = 0 ; y < currentGrid[x].length ; y++)
                    {
                        int b = checkNeighbors(y, x);
                        if(b > 4 || b < 2)
                            newGrid[x][y] = black;
                        else
                            newGrid[x][y] = white;
                    }
                if(!getRun())
                    timer.stop();
            }
        });
        */
    }

    public static void main(String[] args)
    {
        new CA_Driver();
    }

    private int checkNeighbors(int w, int h)
    {
        int b = 0;
        //Top Left
        if((w != 0) && (h != 0) && (currentGrid[h - 1][w - 1] == black))
            b++;
        //Top Middle
        if((h != 0) && (currentGrid[h - 1][w] == black))
            b++;
        //Top Right
        if((w != width - 1) && (h != 0) && (currentGrid[h - 1][w + 1] == black))
            b++;
        //Middle Left
        if((w != 0) && (currentGrid[h][w - 1] == black))
            b++;
        //Middle Right
        if((w != width - 1) && (currentGrid[h][w + 1] == black))
            b++;
        //Bottom left
        if((w != 0) && (h != height - 1) && (currentGrid[h + 1][w - 1] == black))
            b++;
        //Bottom Middle
        if((h != height - 1) && (currentGrid[h + 1][w] == black))
            b++;
        //Bottom Right
        if((w != width - 1) && (h != height - 1) && 
                        (currentGrid[h + 1][w + 1] == black))
            b++;
        return b;
    }

    private class Cell extends JPanel
    {
        private Color c;
        private int posx, posy;

        public Cell(int x, int y)
        {
            posx = x;
            posy = y;
        }

        public Point getLocation()
        {
            return new Point(posx, posy);
        }

        public void setColor()
        {
            c = newGrid[posx][posy];
            setBackground(c);
        }

        public Dimension getPreferredSize()
        {
            return new Dimension(10, 10);
        }
    }
}

我认为在这种情况下使用SwingWorker不合适。currentGrid变量在EDT上用于绘制。在后台线程上修改它会导致线程问题。 - Robin
我同意,尽管我打算保留我的答案。SwingWorker 是朝着正确方向迈出的一步。 - mrres1

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接