0

I have the following files in my project:

  • main.java for starting up the application
  • RootLayout.fxml & RootlayoutController.java which serves as the main stage and will be used for a menu bar
  • Overview.fxml and OverviewController.java for the main window of the application
  • point.java which contains the application logic

The OverviewController's test()-method is triggered by a Button's onAction-Event. Essentially I am looking for a way to give the Point.java class access to the OverviewController.java class, so it can call the associated drawPoint(double x, double y) method.

I have been researching this question for quite a while now, but have been unable to find an understandable answer - since my knowledge of JavaFX is somewhat limited.

My sincere thanks for taking your time to answer my question.

Main.java

    public class Main extends Application {

    public Stage primaryStage;
    private BorderPane rootLayout;

    public Main(){

    }




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

            this.primaryStage = primaryStage;
            this.primaryStage.setTitle("");
            initRootLayout();
            showOverview();

    }

    public void initRootLayout(){
        try {

            // Load root layout from fxml file.
            FXMLLoader loader = new FXMLLoader();
            loader.setLocation(Main.class
                    .getResource("view/RootLayout.fxml"));
            rootLayout = (BorderPane) loader.load();

            // Show the scene containing the root layout.
            Scene scene = new Scene(rootLayout);
            primaryStage.setScene(scene);
            primaryStage.setHeight(900);
            primaryStage.setWidth(900);

            // Give the controller access to the main app.
            RootLayoutController controller = loader.getController();
            controller.setMainApp(this);

            primaryStage.show();
        } catch (IOException e) {
            e.printStackTrace();
        }


    }
    public void showOverview(){
        try {

            // Load Overview
            FXMLLoader loader = new FXMLLoader();
            loader.setLocation(Main.class.getResource("view/Overview.fxml"));
            AnchorPane overview = (AnchorPane) loader.load();

            // Set overview into the center of root layout.
            rootLayout.setCenter(overview);

            // Give the controller access to the main app.
            OverviewController controller = loader.getController();
            controller.setMainApp(this);

        } catch (IOException e) {
            e.printStackTrace();
        }
    }

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

OverviewController.java

   public class OverviewController {

       private sample.Main Main;

       public void setMainApp(Main mainApp) {
           this.Main = mainApp;
       }

       @FXML
       Canvas canvas;

       public void test(){
         Point point = new Point(5,5);
         point.drawPoint();
       }

       public void draw(double x, double y){

         GraphicsContext gc = canvas.getGraphicsContext2D();
         gc.setFill(Color.rgb(255, 0, 0));
         gc.fillOval(x-4, y-4, 8, 8);

       }
 }

Point.java

public class Point {

    public double x;
    public double y;

    point(double x, double y){      
      this.x = x;
      this.y = y;      
    }
    drawPoint(){
      // This is where I want to build a reference to OverviewController.java's draw(double x, double y)-Method
    } 
  }
Hips
  • 244
  • 1
  • 9

1 Answers1

2

I would recommend using a Model-View-Controller (or similar pattern) approach, instead of trying to let the data type Point have access to the controller class.

That way you can let any controller that has access to the model generate points. Your OverviewController just has to observe the model (or specific properties it contains) in order to draw the points onto the canvas, etc.

Create a model class:

public class Model {

    private final ObservableList<Point> points = FXCollections.observableArrayList();

    public ObservableList<Point> getPoints() {
        return points ;
    }

    // other data you need to share...

}

Then create an instance and give a reference to that instance to both your controllers:

public class Main extends Application {

    public Stage primaryStage;
    private BorderPane rootLayout;

    private final Model model ;


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

            this.model = new Model();

            this.primaryStage = primaryStage;
            this.primaryStage.setTitle("");
            initRootLayout();
            showOverview();

    }

    public void initRootLayout(){
        try {

            // Load root layout from fxml file.
            FXMLLoader loader = new FXMLLoader();
            loader.setLocation(Main.class
                    .getResource("view/RootLayout.fxml"));
            rootLayout = (BorderPane) loader.load();

            // Show the scene containing the root layout.
            Scene scene = new Scene(rootLayout);
            primaryStage.setScene(scene);
            primaryStage.setHeight(900);
            primaryStage.setWidth(900);

            // Give the controller access to the main app.
            RootLayoutController controller = loader.getController();

            // do you really need your controllers to have access to the main app?
            // it seems like it creates excessive coupling (you can no longer
            // use this fxml-controller pair anywhere else as it has a 
            // dependency on this application class)
            controller.setMainApp(this);

            controller.initModel(model);

            primaryStage.show();
        } catch (IOException e) {
            e.printStackTrace();
        }


    }
    public void showOverview(){
        try {

            // Load Overview
            FXMLLoader loader = new FXMLLoader();
            loader.setLocation(Main.class.getResource("view/Overview.fxml"));
            AnchorPane overview = (AnchorPane) loader.load();

            // Set overview into the center of root layout.
            rootLayout.setCenter(overview);

            // Give the controller access to the main app.
            // WHY???

            OverviewController controller = loader.getController();
            controller.setMainApp(this);

            controller.initModel(model);

        } catch (IOException e) {
            e.printStackTrace();
        }
    }

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

And now you can do:

public class OverviewController {

       private Model model ;

       public void initModel(Model model) {
           this.model = model ;
           this.model.getPoints().addListener((Change<? extends Point> change) -> {
               while (change.next()) {
                   if (change.wasAdded()) {
                       for (Point p : change.getAddedSublist()) {
                           draw(p.x, p.y);
                       }
                   }
               }
           });
       }

       @FXML
       Canvas canvas;

       public void test(){
         Point point = new Point(5,5);
         model.getPoints().add(point);
       }

       public void draw(double x, double y){

         GraphicsContext gc = canvas.getGraphicsContext2D();
         gc.setFill(Color.rgb(255, 0, 0));
         gc.fillOval(x-4, y-4, 8, 8);

       }
 }

And similarly:

public class RootLayoutController {

    private Model model ;

    public void initModel(Model model) {
        this.model = model ;
    }

    // handler that adds a point:
    @FXML
    private void addPoint(MouseEvent e) {
        Point p = new Point(e.getX(), e.getY());
        model.getPoints().add(p);
    }
}
James_D
  • 201,275
  • 16
  • 291
  • 322
  • Also see http://stackoverflow.com/questions/32342864/applying-mvc-with-javafx/, which is very similar – James_D Sep 14 '15 at 19:18
  • Thank you for your answer - it really helped guiding me in the right direction. I have wondered how the implementation would change, if I would like to listen not only Objects which are added or removed from the _ObservableList point_, but also for changes in their property but I may have found the (your) answer already: http://stackoverflow.com/questions/26313756/implementing-an-observablevalue I will also try to research the various hints you gave me in the source code. The link you attached has also helped me in gaining a better understanding of MVC. – Hips Sep 14 '15 at 19:27