【问题标题】:Trigger event only after repaint in Java Swing?仅在 Java Swing 中重绘后触发事件?
【发布时间】:2012-03-22 00:43:55
【问题描述】:

我正在用 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?

【问题讨论】:

  • 您应该避免让程序逻辑依赖于绘画,因为您无法完全控制绘画何时甚至是否会发生。与其重绘图像并调用repaint(),不如在程序启动时简单地将掷骰子图像放入ImageIcons,然后在Swing Timer 中交换JLabel 中的图标?然后当延迟足够长时停止你的计时器,如果阻塞,移动你的棋子。

标签: java swing timer repaint


【解决方案1】:

所以骰子会随机显示几次,然后慢慢确定一个数字。完成后,我想调用 movePiece() 方法。然而,实际上,重绘偶尔会发生,并且会搞砸一切,以便在掷骰子实际完成动画之前调用 movePiece()。

这里让我担心的是为什么你的画会偶尔出现——它根本不应该那样做,也许是你需要修复的。我想知道您是否在每次绘图时都从文件中读取图像,或者其他一些导致绘图速度变慢的原因。如果您需要有关此问题的更多帮助,则必须向我们提供有关如何进行绘画的更多信息。无论如何,您应该避免让程序逻辑依赖于绘画,因为您无法完全控制绘画何时甚至是否会发生。

与其重绘图像并调用 repaint(),不如在程序启动时简单地将掷骰子图像放入 ImageIcons,然后在 Swing Timer 中交换 JLabel 中的图标?然后当延迟足够长时停止你的计时器,如果阻塞,移动你的棋子。

因此,假设您有几个骰子,每个骰子都可以由一个 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() 时的逻辑,我认为这应该保持不变。

【讨论】:

  • 谢谢,这让我的生活变得轻松了很多。
【解决方案2】:

您可以在另一个线程中调用滚动并将当前线程加入()到滚动线程。这样主代码将等到滚动线程死亡(完成滚动)。

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。这不是把同样的问题转移到另一个线程吗?
【解决方案3】:

如果您在SwingUtilities.invokeLater(Runnable); 中调用movePiece();,会有什么变化吗?

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

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

【讨论】:

    猜你喜欢
    • 2011-09-17
    • 2014-05-13
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-09-27
    • 1970-01-01
    相关资源
    最近更新 更多