2015-09-15 35 views
-1

我正嘗試在JavaFX中創建一個項目 - 由點表示的機器人 - 在基於某種通信協議的畫布上移動。到目前爲止,我已經發現,遵循MVC方法,我應該將相關數據(機器人)保存在ObservableList類型的對象中,然後控制器可以使用它來更新畫布。JavaFX - 通過更改畫布上的屬性(位置)繪製對象

運行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

如果您詢問在運行時產生錯誤的代碼,您應該發佈一個實際編譯的示例。這裏有多個編譯錯誤。要創建[MCVE],您應該實際創建代碼,運行ti以驗證它演示您正在處理的問題,然後直接在此處複製代碼。這不是你運行的代碼。由於您使用的是JavaFX屬性,我還建議您遵循[Java FX屬性模式](http://www.oracle.com/pls/topic/lookup?ctx=javase80&id=JFXBD107)。 –

+0

親愛的James_D,謝謝你的回覆。我很感激你的意見,並且遺憾的是沒有仔細檢查我提交的代碼。我修復了所有編譯錯誤,因此現在應該可以運行代碼。該錯誤消息與我之前提交的錯誤消息相同。正如你所建議的,我試着在Robot類中遵循_Java FX Properties pattern_。 – Hips

回答

0

因爲你所觀察的機器人的座標,並改變響應的用戶界面,你必須改變FX應用程序線程上的座標以遵守JavaFX單線程規則。 (您可以想象聽衆會在FX應用程序線程中安排UI的更改,但是您將不得不擔心座標同步,因爲它們將從兩個不同的線程訪問)。

因此,您的run()方法應該像

公共無效的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); 
     } 
    } 
} 

這正好解決眼前的問題;你可能會發現還有其他的。