java - 沿着路径移动对象: How to call actionPerformed only after previous actionPerformed has finished?

标签 java swing timer

我有一个关于 Swing Timer 的问题,特别是如何多次调用 actionPerformed 并等待最后一个 actionPerformed 完成后再执行它。我知道其他人以前也问过这个问题,但我发现没有一个解决方案适合我的程序。

这是我所拥有的: 我正在尝试沿从一个位置到另一个位置的路径使用方形对象(矩形)。路径以正方形对象数组的形式给出(路径是使用 A* 发现的,路径中的所有正方形都添加到 ArrayList 中)。 actionPerformed 方法将方 block 从当前的 x、y 位置移动到下一个位置,下一个是我提到的路径中的下一个方 block 。例如:

起点(移动方 block )位于 x 200,y 50 终点(目标方格)位于 x 300,y 150 到达该位置的路径中的方 block 为(在 x、y 位置): (250, 50), (300, 50), (300, 100), (300, 150)

enter image description here

但是移动的方 block 不是一次移动一个方 block ,而是如下所示移动:(走捷径)。

enter image description here

我相信原因是计时器没有等待 actionPerformed 完成,因此在方 block 开始移动之前,监听器已经接收到最后一个方 block (300, 150) 作为目标,然后移动方 block 直接移动到那里..

这是我的一些代码:

MovementListener(保存actionPerformed)

public MovementListener(Canvas canvas){
        this.canvas = canvas;
    }

public void setSquares(Square moving, Square target){
    this.moving = moving;
    this.target = target;
}

@Override
public void actionPerformed(ActionEvent e) {
    // I tried adding a boolean here, to indicate if the action is finished or not, what I tried was:
    // setRunning(true); (set to false below, see at the end of this method)
    if (this.moving.getX() < this.target.getX()){
        moving.setLocation((int)(moving.getX() + 10), (int)(moving.getY()));
        this.canvas.repaint();
    } 

    if (this.moving.getY() < this.target.getY()){
        moving.setLocation((int)(moving.getX()), (int)(moving.getY() + 10));
        this.canvas.repaint();
    }
    // setRunning(false);
}

这是保存监听器和计时器的类:

public Canvas(){
    // setup ActionListener
    listener = new MovementListener(this);
    timer = new Timer(100, listener);
}


public void startTimer(Square moving, Square target){ 
    // I tried using the isRunning variable here, so that the next actionPerformed doesn't get called unless listener is ready
    if (listener.isReady()){
        listener.setSquares(moving, target);
        timer.start();
    }
}

这是移动 Action 开始的代码(上面定义了 Square moving)。另外,向后的 for 循环是正确的,路径 ArrayList 是相反的顺序(最后一个方 block 位于位置 0,路径的开头位于列表末尾):

for (int i = controller.getPath().size()-1; i >= 0; i--){
     Square target = controller.getPath().get(i);
     canvas.startTimer(moving, target);
}

任何帮助将不胜感激!我已经尝试解决这个问题几个小时了.. 谢谢:)

编辑: 好吧,我按照下面描述的 camickr 的方式让它工作:

新的actionPerformed

@Override
public void actionPerformed(ActionEvent e) {
    Square current = controller.getPath().get(0);
    if (this.moving.getX() < current.getX()){
        moving.setLocation((int)(moving.getX() + 10), (int)(moving.getY()));
        this.canvas.repaint();
    } 

    if (this.moving.getY() < current.getY()){
        moving.setLocation((int)(moving.getX()), (int)(moving.getY() + 10));
        this.canvas.repaint();
    } else {
        controller.getPath().remove(current);
    }

    if (controller.getPath().isEmpty()){
        canvas.getTimer().stop();
    }
}

现在可以了!

最佳答案

for (int i = controller.getPath().size()-1; i >= 0; i--){
     Square target = controller.getPath().get(i);
     canvas.startTimer(moving, target);
}

在上面的代码中,您迭代设置 Square 位置的循环。在计时器有机会触发之前,正方形位置就设置为末尾,因此您只能看到绘制在最后一个位置的正方形。

当您使用计时器时,您不需要使用循环。定时器取代了循环。您只需启动计时器即可。然后,当计时器触发时,您将执行某些操作。

所以,你的逻辑会是这样的:

  1. 将 Square 对象添加到 ArrayList
  2. 启动计时器
  3. 当 ActionListener 被调用时,你 a) 从 ArrayList 中获取第一个 Square b) 将对象移动到 Square c) 从 ArrayList 中删除 Square
  4. 当 ArrayList 为空时,您将停止计时器。

关于java - 沿着路径移动对象: How to call actionPerformed only after previous actionPerformed has finished?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/49411148/

相关文章:

Java table.getmode.getvalueat(row, column) 在 1 种方法中工作,而不在其他方法中工作?

java - 如何将版本变量从build.gradle文件获取到java spring boot Controller 中?

java - JTextField 在 Mac 上显示,但在 Windows 上不显示

java - 执行一次后停止 Swing 计时器

javascript - 在 jQuery 中每 5 秒调用一次函数的最简单方法是什么?

java - 如何执行一个任务并每分钟返回一个数据

java - 使用 VS Code 在 Spring Boot 中设置 GCP 环境变量

java - 如何修复 java.lang.StringIndexOutOfBoundsException : length=225; regionStart=4; regionLength=-2

java 在操作textField数据时重写textField上的getText方法

java - 在 java swing 组件上绘制边框