我正在尝试在JavaFX中创建一个项目 - 由点表示的机器人 - 基于某些通信协议在画布上移动。到目前为止,我已经发现遵循MVC方法,我应该将相关数据(机器人)保存在ObservableList类型的Object中,然后控制器可以使用它来更新画布。
运行Controller.java的test()方法我得到以下错误,我无法解决。我已经注释了Controller.java中的相关行 - 删除它也解决了错误消息。
我意识到这种方法可能与最佳实践相去甚远。我将非常感谢有关如何解决此问题或更好地使用Observable的任何提示,以允许控制器在更改后在画布上绘制机器人位置。
Main.java
import javafx.application.Application;
import javafx.fxml.FXMLLoader;
import javafx.scene.Scene;
import javafx.scene.layout.BorderPane;
import javafx.stage.Stage;
import java.io.IOException;
public class Main extends Application {
public Stage primaryStage;
private BorderPane rootLayout;
private final Model model = new Model();
@Override
public void start(Stage primaryStage) throws Exception{
// this.model = new Model();
this.primaryStage = primaryStage;
this.primaryStage.setTitle("");
initRootLayout();
}
public void initRootLayout(){
try {
// Load root layout from fxml file.
FXMLLoader loader = new FXMLLoader();
loader.setLocation(Main.class.getResource("Overview.fxml"));
rootLayout = (BorderPane) loader.load();
// Show the scene containing the root layout.
Scene scene = new Scene(rootLayout);
primaryStage.setScene(scene);
primaryStage.setHeight(500);
primaryStage.setWidth(500);
// Give the controller access to the main app.
Controller controller = loader.getController();
controller.initModel(model);
primaryStage.show();
} catch (IOException e) {
e.printStackTrace();
}
}
public static void main(String[] args) {
launch(args);
}
}
Model.java
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
public class Model {
private final ObservableList<Robot> robots = FXCollections.observableArrayList();
public ObservableList<Robot> getRobots(){ return robots;}
}
Robot.java
import javafx.beans.property.DoubleProperty;
import javafx.beans.property.IntegerProperty;
import javafx.beans.property.SimpleDoubleProperty;
import javafx.beans.property.SimpleIntegerProperty;
import java.util.Random;
public class Robot implements Runnable {
private IntegerProperty id = new SimpleIntegerProperty();
public void setID(int id){this.id.set(id);}
public int getId(){ return this.id.get();};
private Memory memory;
private DoubleProperty positionX = new SimpleDoubleProperty();
public double getPositionX() {return positionX.get();};
public DoubleProperty positionXProperty(){ return positionX;}
private DoubleProperty positionY = new SimpleDoubleProperty();
public double getPositionY() {return positionY.get();};
public DoubleProperty positionYProperty(){ return positionY;}
public void setPosition (double x, double y){
this.positionX.set(x);
this.positionY.set(y);
this.memory.setPosition(x,y);
}
// Constructor
public Robot(int id, double x, double y){
this.setID(id);
memory = new Memory();
this.setPosition(x, y);
}
public double[] getPosition(){
double [] ret = new double[2];
ret[0] = this.memory.getPosition()[0];
ret[1] = this.memory.getPosition()[1];
return ret;
}
public void move (double targetx, double targety){
System.out.println("Agent carrying " + this.getId + " has moved from " + this.getPositionX() + " / " + this.getPositionY() + " to " + targetx + " / " + targety + " ." );
this.setPosition(targetx, targety);
}
public void run(){
while(true){
// the robot gets assigned a new position based on a communication
// protocoll used by the Robots which I have removed for convenience
Random r = new Random();
double nextX = 0 + (100) * r.nextDouble();
double nextY = 0 + (100) * r.nextDouble();
this.move(nextX, nextY);
try{
Thread.sleep(2000);
}
catch(InterruptedException e){
System.out.println(e);
}
}
}
}
Memory.java
public class Memory {
double[] position = new double[2];
double[] plast = new double[2];
// constructor
public Memory(){
double[] position = new double[2];
double[] plast = new double[2];
}
public double[] getPosition(){
return position;
}
public double[] getLastPosition(){
return plast;
}
public void setLastPosition(double x, double y){
plast[0] = x;
plast[1] = y;
}
public void setPosition(double x, double y){
position[0] = x;
position[1] = y;
}
}
Controller.java
import javafx.beans.value.ChangeListener;
import javafx.beans.value.ObservableValue;
import javafx.fxml.FXML;
import javafx.scene.canvas.Canvas;
import javafx.scene.canvas.GraphicsContext;
import javafx.scene.paint.Color;
public class Controller {
private Model model;
public void initModel(Model model) {
this.model = model ;
for (Robot robot : model.getRobots()){
robot.positionXProperty().addListener(new ChangeListener<Number>() {
@Override
public void changed(ObservableValue<? extends Number> observable, Number oldValue, Number newValue) {
System.out.println("Observed change in value X of Robot carrying ID: " + robot.getId());
drawPoint(robot.getPositionX(), robot.getPositionY());
// if I comment out the above line I get no errors
}
});
robot.positionYProperty().addListener(new ChangeListener<Number>() {
@Override
public void changed(ObservableValue<? extends Number> observable, Number oldValue, Number newValue) {
System.out.println("Observed change in value Y of Robot carrying ID: " + robot.getId());
drawPoint(robot.getPositionX(), robot.getPositionY());
// if I comment out the above line I get no errors
}
});
}
}
@FXML
Canvas canvas;
public void test(){
GraphicsContext gc =canvas.getGraphicsContext2D();
gc.strokeRect(0,0,100,100);
Robot a = new Robot(0,25,25);
Robot b = new Robot(1,50,50);
model.getRobots().add(a);
model.getRobots().add(b);
Thread ta = new Thread(a);
Thread tb = new Thread(b);
initModel(model);
System.out.println("Starting threads.");
ta.start();
tb.start();
}
public void drawPoint(double x, double y){
GraphicsContext gc = canvas.getGraphicsContext2D();
gc.setFill(Color.rgb(255, 0, 0));
gc.fillOval(x, y, 5, 5);
}
}
Overview.fxml
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.scene.canvas.*?>
<?import javafx.scene.control.*?>
<?import java.lang.*?>
<?import javafx.scene.layout.*?>
<BorderPane maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" prefHeight="400.0" prefWidth="600.0" xmlns="http://javafx.com/javafx/8.0.40" xmlns:fx="http://javafx.com/fxml/1" fx:controller="Controller">
<top>
<Button fx:id="buttonTest" mnemonicParsing="false" onAction="#test" text="test()" BorderPane.alignment="CENTER" />
</top>
<center>
<Canvas fx:id="canvas" height="200.0" width="200.0" BorderPane.alignment="CENTER" />
</center>
</BorderPane>
错误:
java.lang.InternalError: Unrecognized PGCanvas token: -96
at com.sun.javafx.sg.prism.NGCanvas.renderStream(NGCanvas.java:1146)
at com.sun.javafx.sg.prism.NGCanvas.renderContent(NGCanvas.java:595)
at com.sun.javafx.sg.prism.NGNode.doRender(NGNode.java:2067)
at com.sun.javafx.sg.prism.NGNode.render(NGNode.java:1959)
at com.sun.javafx.sg.prism.NGGroup.renderContent(NGGroup.java:235)
at com.sun.javafx.sg.prism.NGRegion.renderContent(NGRegion.java:576)
at com.sun.javafx.sg.prism.NGNode.doRender(NGNode.java:2067)
at com.sun.javafx.sg.prism.NGNode.render(NGNode.java:1959)
at com.sun.javafx.tk.quantum.ViewPainter.doPaint(ViewPainter.java:474)
at com.sun.javafx.tk.quantum.ViewPainter.paintImpl(ViewPainter.java:327)
at com.sun.javafx.tk.quantum.PresentingPainter.run(PresentingPainter.java:91)
at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:511)
at java.util.concurrent.FutureTask.runAndReset(FutureTask.java:308)
at com.sun.javafx.tk.RenderJob.run(RenderJob.java:58)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)
at com.sun.javafx.tk.quantum.QuantumRenderer$PipelineRunnable.run(QuantumRenderer.java:125)
at java.lang.Thread.run(Thread.java:745)
答案 0 :(得分:0)
因为您正在观察机器人的坐标并更改响应中的UI,所以您必须更改FX应用程序线程上的坐标才能遵守JavaFX单线程规则。 (可以想象,听众会在FX应用程序线程上安排对UI的更改,但是您必须担心坐标上的同步,因为它们将从两个不同的线程访问。)
因此,您的run()
方法应该是
public void run(){
while(true){
// the robot gets assigned a new position based on a communication
// protocoll used by the Robots which I have removed for convenience
Platform.runLater(() -> {
Random r = new Random();
double nextX = 0 + (100) * r.nextDouble();
double nextY = 0 + (100) * r.nextDouble();
this.move(nextX, nextY);
});
try{
Thread.sleep(2000);
}
catch(InterruptedException e){
System.out.println(e);
}
}
}
这解决了眼前的问题;你可能会发现还有其他人。