GUI线程更新问题

eat*_*hil 1 java user-interface swing multithreading

我有一个GUI类Gui:

public class Gui extends JFrame implements Runnable
{

private JPanel outer, inner;
private JLabel[][] labels = new JLabel[22][12];
private Color[][] defaultMap, map;
Thread t;
private int row, col;
private Color color;

public Gui()
{
    Container content = getContentPane();
    content.setLayout(new BorderLayout());

    setBackground(Color.BLACK);
    setSize(1000, 1000);
    setLocation(300, 0);
    setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    setResizable(false);

    defaultMap = createMap();
    draw(defaultMap);
}

public Color[][] createMap()
{
    Color[][] map = new Color[22][12];
    for (int i = 0; i < 22; i++)
    {
        for (int j = 0; j < 12; j++)
        {
            map[i][j] = Color.WHITE;
        }
    }
    for (int i = 0; i < 22; i++)
    {
        map[i][0] = Color.GRAY;
        map[i][11] = Color.GRAY;
    }
    for (int i = 0; i < 12; i++)
    {
        map[0][i] = Color.GRAY;
        map[21][i] = Color.GRAY;
    }
    return map;
}

public void draw(Color[][] map)
{

    outer = new JPanel();
    outer.setLayout(new BorderLayout());
    outer.setBackground(Color.WHITE);
    outer.setPreferredSize(new Dimension());

    inner = new JPanel();
    inner.setLayout(new GridLayout(22, 12, 2, 2));
    inner.setBackground(Color.BLACK);

    for (int i = 0; i < 22; i++)
    {
        for (int j = 0; j < 12; j++)
        {
            labels[i][j] = new JLabel();
            JLabel label = labels[i][j];
            label.setPreferredSize(new Dimension(20, 20));
            label.setBackground(map[i][j]);
            label.setOpaque(true);
            inner.add(label);
        }
    }

    add(outer);
    add(inner);
    pack();
}

public void move(int row, int col, Color color)
{
    System.out.println(row+","+col);
    map = defaultMap;
    map[row][col] = color;
    t = new Thread(this);
    t.start();
}

@Override
public void run()
{
    draw(map);
}
}
Run Code Online (Sandbox Code Playgroud)

这是从我的主类调用的,如下所示:

public static void main(String[] args)
{
    SwingUtilities.invokeLater(new Runnable()
    {
        @Override
        public void run()
        {
            try
            {
                gui = new Gui();
                gui.setVisible(true);
                gui.move(2,5,Color.GREEN);
                Thread.sleep(1000);
                gui.move(3,5,Color.GREEN);
                Thread.sleep(1000);
                gui.move(4,5,Color.GREEN);
            } catch (InterruptedException ex)
            {
                Logger.getLogger(Tetris.class.getName()).log(Level.SEVERE, null, ex);
            }
        }
    });
}
Run Code Online (Sandbox Code Playgroud)

所以当调用gui.move()函数时会发生奇怪的事情.如果有帮助,您可以忽略其余部分或使用它.但是每一次,绿色区块都应该"添加"到2.5的gui; 3,5; 和4,5; 一秒钟后另一个.

问题:

Gui一段时间保持黑色,然后立即用正确的网格和颜色重新绘制/更新,前两个块正确地着色为绿色,但它在4,5处丢失了最后一个块.同样,应该立即绘制初始的"defaultMap",但事实并非如此,JFrame是黑色的,直到所有内容一次性绘制在一起减去最后一个绿色块.然后每个绿色块应该在1秒后涂上另一个.

有趣的是,Gui中move方法中的System.out.println()位打印出我想要的行和列...它们在终端中一个接一个地出来.所以这告诉我事情是正确的.但是我不确定Gui发生了什么......

编辑:故事略有不同.经过仔细检查,我注意到,一旦整个地图被绘制,最后一个绿色块就会出现一秒,但会立即"消失"重新绘制白色.

Gui*_*let 6

你正在EDT(事件调度线程)上睡觉.你永远不应该阻止EDT.

  • 永远不要打电话sleepwait在美国东部时间
  • 将所有长时间运行的任务移动到其他线程(使用ExecutorsSwingWorker)
  • 对于UI的重复或延迟更新,您可以使用 javax.swing.Timer
  • 所有UI操作都应该在EDT上执行(使用SwingUtilities.invokeLaterjavax.swing.TimerSwingWorker

swing标签wiki中阅读有关Swing并发的更多信息.