当前位置: 首页 > 知识库问答 >
问题:

JavaFX从另一个线程更新AnchorPane

高嘉熙
2023-03-14

所以我在这个问题上有点卡住了。我有一个非常基本的游戏,你用箭头键在网格周围移动一艘船。

我添加了另一个线程与一些怪物,应该自动漫游网格。我可以从打印语句中看到线程正在运行怪物正在移动,但是,图像位置没有更新。

我发现了一些类似的问题,有很多建议可以使用Platfrom。稍后运行。但我不确定它是否符合我的具体情况,如果符合,如何实施。

下面是怪物类正在做的事情,每秒将怪物向右移动一个空间。就像我提到的,每次调用setX()时,我都会记录当前位置,这样我就可以看到位置正在更新。

import javafx.collections.ObservableList;
import javafx.scene.Node;
import javafx.scene.image.Image;
import javafx.scene.image.ImageView;
import java.awt.Point;

public class Monster implements Runnable {

    private Point currentPoint;
    private OceanMap map;

    public Monster(int x, int y) {
        this.currentPoint = new Point(x, y);
        this.map = OceanMap.getInstance();
    }

    public Point getLocation() {
        System.out.println(this.currentPoint.toString());
        return this.currentPoint;
    }

    private void setNewLocation(Point newLocation) {
        this.currentPoint = newLocation;
    }

    private void setY(int newY) {
        this.currentPoint.y = newY;
        this.setNewLocation(new Point(this.currentPoint.x, this.currentPoint.y));
    }

    private void setX(int newX) {
        this.currentPoint.x = newX;
        this.setNewLocation(new Point(this.currentPoint.x, this.currentPoint.y));
        System.out.println(this.currentPoint.toString());
    }

//    public void addToPane() {
//        System.out.println("this is called");
//        iv.setX(this.currentPoint.x + 1 * 50);
//        iv.setY(this.currentPoint.y * 50);
//        obsrvList.add(iv);
//    }

    @Override
    public void run() {
        while (true) {
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            this.setX(this.currentPoint.x + 1);
        }

    }

}

这里是JavaFX线程。

/* Monster resources */
private Image monsterImage = new Image(getClass().getResource("monster.png").toExternalForm(), 50, 50, true, true);
private ImageView monsterImageView1 = new ImageView(monsterImage);
private Monster monster1;
private Thread monsterThread;

@Override
public void start(Stage oceanStage) throws Exception {

    root = new AnchorPane();
    scene = new Scene(root, scale * xDimensions, scale * yDimensions);

    oceanStage.setScene(scene);
    oceanStage.setTitle("Ocean Explorer");

    /* Draw Grid */
    for (int x = 0; x < xDimensions; x++) {
        for (int y = 0; y < yDimensions; y++) {
            Rectangle rect = new Rectangle(x * scale, y * scale, scale, scale);
            rect.setStroke(Color.BLACK);
            rect.setFill(Color.PALETURQUOISE);
            root.getChildren().add(rect);
        }
    }

    oceanStage.show();

    monsterThread = new Thread(monster1);
    monsterThread.start();
    Platform.runLater(() -> {
        monsterImageView1.setX(monster1.getLocation().x * scale);
        monsterImageView1.setY(monster1.getLocation().y * scale);
        root.getChildren().add(monsterImageView1);
    });

    startSailing();
}

如果需要,我可以提供更多的代码,这是目前我认为相关的一切。

同样,我的问题是,如何从另一个线程更新JavaFX线程的UI?

共有1个答案

汪成仁
2023-03-14

Monster内部更新currentPoint时,此值永远不会传播到monsterImageView1。您应该将currentPoint转换为属性,然后绑定到它:

class Point {
    final int x;
    final int y;

    Point(int x, int y) {
        this.x = x;
        this.y = y;
    }
}

class Monster implements Runnable {
    private ReadOnlyObjectWrapper<Point> location = new ReadOnlyObjectWrapper<>();

    public Monster(int x, int y) {
        setLocation(new Point(x, y));
    }

    public Point getLocation() {
        return this.location.get();
    }

    private void setLocation(Point location) {
        this.location.set(location);
    }

    public ReadOnlyProperty<Point> locationProperty() {
        return this.location.getReadOnlyProperty();
    }

    private void setY(int newY) {
        this.setLocation(new Point(this.getLocation().x, newY));
    }

    private void setX(int newX) {
        this.setLocation(new Point(newX, this.getLocation().y));
    }

    @Override
    public void run() {
        while (true) {
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }

            // run the update on the FX Application Thread for thread safety as well as to prevent errors in certain cases
            Platform.runLater(() -> this.setX(this.getLocation().x + 1));
        }
    }
}

monsterThread = new Thread(monster1);
monsterThread.start();
monsterImageView1.xProperty().bind(Bindings.createIntegerBinding(() -> monster1.getLocation().x * scale, monster1.locationProperty()));
monsterImageView1.yProperty().bind(Bindings.createIntegerBinding(() -> monster1.getLocation().y * scale, monster1.locationProperty()));
root.getChildren().add(monsterImageView1);

然而,正如@James_D所提到的,时间线将是以正确的方式解决这一问题的更好方法

class Monster {
    private ReadOnlyObjectWrapper<Point> location = new ReadOnlyObjectWrapper<>();
    private Timeline timeline;

    public Monster(int x, int y) {
        setLocation(new Point(x, y));

        timeline = new Timeline(new KeyFrame(Duration.seconds(1), event -> {
            setX(getLocation().x + 1);
        }));
        timeline.setCycleCount(Timeline.INDEFINITE);
    }

    public void start() {
        timeline.play();
    }

    // NOTE: remember to call stop() or this will result in a memory leak
    public void stop() {
        timeline.stop();
    }

    public Point getLocation() {
        return this.location.get();
    }

    private void setLocation(Point location) {
        this.location.set(location);
    }

    public ReadOnlyProperty<Point> locationProperty() {
        return this.location.getReadOnlyProperty();
    }

    private void setY(int newY) {
        this.setLocation(new Point(this.getLocation().x, newY));
    }

    private void setX(int newX) {
        this.setLocation(new Point(newX, this.getLocation().y));
    }
}
 类似资料:
  • 问题内容: 我有一个javafx应用程序和一个工作线程,通过来实现,它执行一个漫长的过程,即压缩并上传一组文件。 我已通过将任务进度连接到进度条。 除此之外,我想将有关正在处理的项目的详细状态报告到ui中。 也就是说,正在处理的文件的名称及其大小以及单个文件处理可能引起的任何错误。 用这些信息更新UI不能从工作线程中完成,最多我可以将其添加到同步集合中。 但是然后我需要一些事件来通知UI新数据可用

  • 我有一个javafx应用程序和一个通过实现的工作线程,它执行一个很长的过程,即压缩和上载一组文件。 我已通过将任务进度连接到进度条。 除此之外,我还希望将正在处理的项的详细状态报告到UI中。即正在处理的文件的名称及其大小和单个文件进程可能产生的任何错误。 不能从辅助线程中使用这些信息更新UI,最多只能将其添加到同步集合中。 但我需要一些事件来通知UI新数据可用。 javafx是否对此问题有特定的支

  • 问题内容: 亲爱的大家,我遇到了挥杆相关的问题。由于违反公司政策,我无法共享代码,因此我将尽力解释该问题。 简而言之,我创建了一个扩展包含JLabel的JWindow的类。JLabel的文本是通过计时器对象随机更新的,该计时器对象使用scheduleAtFixedRate方法每50毫秒实例化一个TimerTask。JLabel中的值是通过在一个单独的线程(称为传输线程)中调用一个方法来检索的,该线

  • 问题内容: 在我的Java应用程序中,当调用主模块时,我在单独的线程中启动SWT GUI。我需要在主线程中执行一些长时间的操作并更新GUI线程。当我尝试从主线程更新GUI线程(即更改标签文本等)时,出现了。根据我在线阅读的内容,因为SWT不允许非UI线程更新UI对象。如何从主线程更新GUI线程。 我在网上找到了一些示例,但是它们都处理了GUI在主线程中运行并且长时间操作在单独的线程中的情况。我的情

  • 我正在尝试使用来自另一个实现的类。我的所有GUI都在一个类中(我的文本区域就在这个类中),我正在创建一个线程,并在创建GUI时运行它。我正在尝试使用

  • 问题内容: 我想更改Android中的UI。 我的主类创建第二个类,然后第二个类调用主类的方法。主类中的方法应更新UI,但程序在运行时会崩溃。 我该怎么办? 我的主要班级: 还有我的第二堂课: 问题答案: 您不能从主线程以外的线程调用UI方法。您应该使用Activity#runOnUiThread()方法。