【问题标题】:Multiple swing timers多个摆动计时器
【发布时间】:2012-05-30 03:59:02
【问题描述】:

我有(应该是什么)一个简单的问题要解决,我愿意接受其他方法来解决它。我愿意接受其他解决方案。

问题: 我们正在使用 java swing 来显示基于回合、基于图块的游戏的图形。我正在使用带有图标的 jlabels,绝对定位。

为了使运动动画化,我使用了一个摆动计时器,它一次将位置更新 4 个像素,缓慢地向右、向左等移动精灵。

为了实现这一点,我运行了一个计时器,效果非常好。当我尝试向下移动,然后向右移动时,问题就出现了。

精灵向下移动,从不向右移动,如果我通过一些控制台打印来观察执行,可以清楚地看到两个计时器同时运行。我已经在互联网上进行了大量的挖掘工作,但我无法找到一种方法来告诉摆动计时器在第一个计时器停止之前不要执行,如果我尝试忙等待一个计时器完成(糟糕)UI 根本不显示(显然是朝着错误的方向迈出了一步。)

现在我可以完全放弃计时器,要么让精灵传送到它的新位置,要么使用一些糟糕的忙碌等待移动方案,但我希望有好心人有一个解决方案。

简而言之:我需要一种方法来在设定的时间段内运行摇摆计时器,停止它,然后然后启动一个新的计时器,这样它们就不会重叠。最好这种方法允许每个计时器在自己的方法中,然后我可以一个接一个地调用这些方法。

提前感谢您的任何建议。

编辑:扩展的示例代码。如果您的建议需要完整的 scsse,那么很抱歉浪费了您的时间,因为完整的代码是一头野兽。这个示例代码根本不能正常工作,抱歉,但它应该说明这一点。

所以。我们有两个函数,每个函数都有一个运行动画循环的计时器,一个用于向下和向右对角线移动,一个用于直线向下移动。

public class TestClass {
    static int counter = 0;
    static int counter2 = 0;
    static Timer timerC;
    static Timer timerX;

    public static void main(String[] args) {
        moveC();
        moveX();
    }

    public static void moveC() {
        int delay = 200; // milliseconds

        timerC = new Timer(delay, null);
        timerC.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent evt) {
                if (counter < 32) {
                    counter = counter + 4;
                    System.out.println("*C*");
                } else {
                    timerC.stop();
                    System.out.println("*C STOP*");
                }

            }
        });
        timerC.start();
    }

    public static void moveX() {
        int delay = 200; // milliseconds

        timerX = new Timer(delay, null);
        timerX.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent evt) {
                if (counter < 32) {
                    counter = counter + 4;
                    System.out.println("*X*");
                } else {
                    timerX.stop();
                    System.out.println("*X STOP*");
                }

            }
        });
        timerX.start();
    }

}

我最终希望在这里看到的是

*C*
*C*
*C*
*C*
*C STOP*
*X*
*X*
*X*
*X*
*X STOP*

我真正得到的是

*C*
*X*
*C*
*X*
*C*
*X*
*C*
*X*
*C STOP*
*X STOP*

我在这里试图达到的目的是运行一个动画周期以完成,然后是另一个。

再次感谢。

【问题讨论】:

  • 如需尽快获得更好的帮助,请发帖SSCCE
  • “两个计时器同时运行”是什么意思。我只看到一个 Timer 的代码,并且该 Timer 运行一次。请澄清,并考虑创建和发布sscce。另外,请清理您的代码格式,因为如果您的代码更易于阅读和理解,我们将更有机会为您提供好的建议。
  • 我自己会为此使用队列。

标签: java swing timer


【解决方案1】:

不要使用多个 Timer,而是只使用一个 Timer 来根据需要处理每个方向。您需要某种类型的队列来保存方向信息,可以是正式队列,也可以是用作队列的集合(先进先出),然后让您的 Timer 在该队列运行时从该队列中提取方向。例如,这里我使用我的 JList 的模型作为我的队列,方法是删除并使用首先添加的 Direction(在 JList 的顶部):

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

public class TimerPlay extends JPanel {
   private DefaultListModel directionJListModel = new DefaultListModel();
   private JList directionJList = new JList(directionJListModel);
   JButton startTimerButton = new JButton(
         new StartTimerBtnAction("Start Timer"));

   public TimerPlay() {
      ActionListener directionBtnListener = new ActionListener() {

         @Override
         public void actionPerformed(ActionEvent actEvt) {
            String actionCommand = actEvt.getActionCommand();
            Direction dir = Direction.valueOf(actionCommand);
            if (dir != null) {
               directionJListModel.addElement(dir);
            }
         }
      };
      JPanel directionBtnPanel = new JPanel(new GridLayout(0, 1, 0, 10));
      for (Direction dir : Direction.values()) {
         JButton dirBtn = new JButton(dir.toString());
         dirBtn.addActionListener(directionBtnListener);
         directionBtnPanel.add(dirBtn);
      }

      add(directionBtnPanel);
      add(new JScrollPane(directionJList));
      add(startTimerButton);
   }

   private class StartTimerBtnAction extends AbstractAction {
      protected static final int MAX_COUNT = 20;

      public StartTimerBtnAction(String title) {
         super(title);
      }

      @Override
      public void actionPerformed(ActionEvent arg0) {
         startTimerButton.setEnabled(false);

         int delay = 100;
         new Timer(delay, new ActionListener() {
            private int count = 0;
            private Direction dir = null;

            @Override
            public void actionPerformed(ActionEvent e) {
               if (count == MAX_COUNT) {
                  count = 0; // restart
                  return;
               } else if (count == 0) {
                  if (directionJListModel.size() == 0) {
                     ((Timer)e.getSource()).stop();
                     startTimerButton.setEnabled(true);
                     return;
                  }
                  // extract from "queue"
                  dir = (Direction) directionJListModel.remove(0);
               }
               System.out.println(dir); // do movement here
               count++;

            }
         }).start();
      }
   }

   private static void createAndShowGui() {
      TimerPlay mainPanel = new TimerPlay();

      JFrame frame = new JFrame("TimerPlay");
      frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
      frame.getContentPane().add(mainPanel);
      frame.pack();
      frame.setLocationByPlatform(true);
      frame.setVisible(true);
   }

   public static void main(String[] args) {
      SwingUtilities.invokeLater(new Runnable() {
         public void run() {
            createAndShowGui();
         }
      });
   }

}

enum Direction {
   UP, DOWN, LEFT, RIGHT;
}

【讨论】:

  • +1 表示sscceMAX_COUNT 是用于演示的任意值吗?
  • @trash:是的,完全正确。它很可能是一个变量。谢谢。 1+ backatcha 的好建议。
  • 谢谢,我绝对可以为我完成这项工作。很有用。保重!
【解决方案2】:

作为参考,这个example 管理四个Timer 实例,其中两个在悬停在任何角落时运行(交错)。您可以将其与您的方法进行比较。这个相关的answer 讨论了类似基于图块的游戏中的动画。

【讨论】:

    【解决方案3】:
    • 将所有Icons 放入某种形式的数组中

    • 创建一个具有短暂延迟的单一 Swing Timer

    • 在 Swing ActionListener 中,从数组中取出每个 `Icon,从屏幕获取边界,将 Icon 移动一步

    • 重复直到达到目标。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2021-01-16
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多