Java Swing中如何在重绘后触发事件?

3

我正在用java制作一个简单的桌游,我想要模拟掷骰子的过程。因此我要像这样闪现出一个骰子的图片:

public Timer roll_dice = new Timer(50, this);
...
public void actionPerformed(ActionEvent evt) {
        if(roll_dice.getDelay() > 500){
            roll_dice.setDelay(50);
            roll_dice.stop();
            movePiece();
        }else{
            roll_dice.setDelay(roll_dice.getDelay() + 50);
            dice_panel.repaint(0);
        }
    }
}

movePiece(){
    //do some more painting
}

因此,骰子会随机显示几次数字,然后慢慢停在一个数字上。完成后,我想调用movePiece()方法。但是,目前情况下,重新绘制发生得不规律,会破坏一切,导致在骰子动画实际完成之前就调用了movePiece()

有没有人有任何想法,如何在最终的重新绘制发生后才调用movePiece?


1
你应该避免让程序逻辑依赖于绘画,因为你无法完全控制何时以及是否会发生绘画。与其重新绘制图像并调用“repaint()”,为什么不在程序启动时将滚动骰子图像放入ImageIcons中,然后在Swing计时器中,在JLabel中交换图标?当延迟足够长,并且在该if块中移动您的内容时,请停止计时器。 - Hovercraft Full Of Eels
3个回答

1
所以骰子会随机显示几次,然后慢慢停留在一个数字上。完成后,我想调用movePiece()方法。但是,目前情况是repaint出现间歇性问题,导致movePiece()在骰子滚动动画完成之前被调用。

我担心的是你这里的绘制出现间歇性问题——它根本不应该出现这种情况,也许你需要解决的就是这个。我想知道是否每次进行绘制时都从文件中读取图像或其他减缓绘制速度的原因。如果您需要更多关于此问题的帮助,则必须向我们提供有关您如何进行绘制的更多信息。无论如何,您都应该避免将程序逻辑依赖于绘制,因为您无法完全控制何时甚至是否会进行绘制。

与其重绘图像并调用repaint(),不如在程序启动时将滚动的骰子图像放入ImageIcons中,在您的Swing Timer中交换JLabel中的图标。当延迟足够长时停止计时器,在if块中移动您的棋子。

假设您有几个骰子,每个骰子都可以由一个JLabel显示,这些JLabel存储在名为diceLabels的JLabel数组中,而ImageIcons可以存储在名为diceIcons的数组中。然后您可以执行以下操作:
  public void actionPerformed(ActionEvent e) {
     if (roll_dice.getDelay() > 500) {
        roll_dice.setDelay(50);
        roll_dice.stop();
        movePiece(); // I like this -- this shouldn't change
     } else {
        roll_dice.setDelay(roll_dice.getDelay() + 50);
        // dice_panel.repaint(0);
        for (JLabel dieLabel : diceLabels) {
           int randomIndex = random.nextInt(diceIcons.length);
           dieLabel.setIcon(diceIcons[randomIndex]);
        }
     }
  }

我喜欢你关于何时调用 movePiece() 的逻辑,并认为这应该保持不变。


谢谢,这让我的生活变得更加轻松了。 - wfbarksdale

0
你可以在另一个线程中调用rolling并将当前线程加入到rolling线程中,这样主代码就会等待直到roll线程结束(完成rolling)。
public void actionPerformed(ActionEvent evt) {
    if(roll_dice.getDelay() > 500){
        Thread rollerThread = new RollerThread();
        rollerThread.start();
        rollerThread.join();
        movePiece();
    }
    else{
        roll_dice.setDelay(roll_dice.getDelay() + 50);
        dice_panel.repaint(0);
    }
}

private RollerThread extends Thread
{
    public void run(){
        roll_dice.setDelay(50);
        roll_dice.stop();
    }
}

然而,这可能无法在EDT上工作,因为重绘应该被安排到队列中。也许您可以使用SwingUtilities.invokeAndWait()调度事件:

public void actionPerformed(ActionEvent evt) {
    Thread thread = new Thread(){
        public void run(){
            if(roll_dice.getDelay() > 500){
                SwingUtilities.invokeAndWait(new Runnable(){
                     public void run(){
                         roll_dice.setDelay(50);
                         roll_dice.stop();
                     }
                });
                movePiece();
            }
            else{
                 roll_dice.setDelay(roll_dice.getDelay() + 50);
                 dice_panel.repaint(0);
            }
        }
    };
    thread.start();
}

我不确定那会解决问题,因为repaint被间歇性地调用。那不是只是把同样的问题移动到另一个线程吗? - wfbarksdale

0

如果你把调用 movePiece(); 的代码放在 SwingUtilities.invokeLater(Runnable); 中,会有什么变化吗?

if(roll_dice.getDelay() > 500){
    roll_dice.setDelay(50);
    roll_dice.stop();

    SwingUtilities.invokeLater(new Runnable() {
        public void run() { movePiece(); }
    });
}
...

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