0

I'm using JavaFX to create Android/Iphone applications by using the Gluon framework.

I know that this type of "how can I get the controller class" question. But this is different.

I know how to get the controller class, but that's is not what I asking for. I asking how I can access the fields from a controller class, without creating any new objects.

Assuming that we have a JavaFX controller class like this:

public class PrimaryPresenter {

    @FXML
    private View primary;

    @FXML
    public LineChart<String, Number> lineChart; // Every time we update the socket, we update the chart too

    public void initialize() {

        primary.setShowTransitionFactory(BounceInRightTransition::new);
        primary.showingProperty().addListener((obs, oldValue, newValue) -> {
            if (newValue) {
                AppBar appBar = MobileApplication.getInstance().getAppBar();
                appBar.setNavIcon(MaterialDesignIcon.MENU.button(e -> 
                        MobileApplication.getInstance().getDrawer().open()));
                appBar.setTitleText("Plot");

            }
        });   

        /*
         * Initial stuffs for the lineChart
         */
        lineChart.setTitle("Adaptive MPC");
    }


}

And I want to access the lineChart object from another class without creating a new PrimaryPresenter controller class. The FXML file is created by Scene Builder.

The reason why I'm asking this question, is because I have another class that loops via a thread and that thread will update the lineChart object in real time.

euraad
  • 2,467
  • 5
  • 30
  • 51
  • You would pass the object to any code that needs it. It'd be better to have a model that's updated and the `LineChart` simply reacts to changes in the model. Make sure the `LineChart` is only ever updated on the UI thread. – Slaw Mar 09 '19 at 00:20
  • @Slaw How? I have tried so many ways to access the fields, but every time I do that, the fields gets another adress. New class means new address. – euraad Mar 09 '19 at 00:30
  • 1
    You mention you know how to get the controller instance (though you say "class", so maybe you don't know?). Get the appropriate instance and query it for the needed objects. – Slaw Mar 09 '19 at 00:32
  • Also take a look at the linked/related questions to that possible duplicate, as well as the related questions to your own question. – Slaw Mar 09 '19 at 00:35
  • @Slaw I'm going to post my suggestion soon in 2-3 min. Then we can discuss about my solution, if it's better than your solution. Because I have tried FXLoader before, and I could only load one FXML file at the time. – euraad Mar 09 '19 at 00:55
  • @Slaw I don't think this question is an duplicate question, because I solved this problem now without using FXLoader. :) – euraad Mar 09 '19 at 01:06

1 Answers1

0

Here is my answer. In every Gluon project, there is a class named GluonApplication and it looks like this:

package com.gluonapplication;

import com.gluonapplication.thread.SocketConnection;
import com.gluonapplication.views.PrimaryView;
import com.gluonapplication.views.SecondaryView;
import com.gluonhq.charm.glisten.application.MobileApplication;
import com.gluonhq.charm.glisten.visual.Swatch;
import javafx.scene.Scene;
import javafx.scene.image.Image;
import javafx.stage.Stage;

public class GluonApplication extends MobileApplication {

    public static final String PRIMARY_VIEW = HOME_VIEW;
    public static final String SECONDARY_VIEW = "Secondary View";

    @Override
    public void init() {
        addViewFactory(PRIMARY_VIEW, () -> new PrimaryView().getView());
        addViewFactory(SECONDARY_VIEW, () -> new SecondaryView().getView());
        DrawerManager.buildDrawer(this);
    }

    @Override
    public void postInit(Scene scene) {
        Swatch.GREEN.assignTo(scene);

        scene.getStylesheets().add(GluonApplication.class.getResource("style.css").toExternalForm());
        ((Stage) scene.getWindow()).getIcons().add(new Image(GluonApplication.class.getResourceAsStream("/icon2.png")));

    }
}

To have access to all the fields form the controller classes. Just do this:

public class GluonApplication extends MobileApplication {

    public static final String PRIMARY_VIEW = HOME_VIEW;
    public static final String SECONDARY_VIEW = "Secondary View";
    private SocketConnection socketConnection;
    private View primaryView; // Add
    private View secondaryView; // Add

    @Override
    public void init() {
        primaryView = new PrimaryView().getView();
        secondaryView = new SecondaryView().getView();
        addViewFactory(PRIMARY_VIEW, () -> primaryView);
        addViewFactory(SECONDARY_VIEW, () -> secondaryView);
        DrawerManager.buildDrawer(this);

        /*
         * This will start the socket connection
         */
        socketConnection = new SocketConnection(primaryView, secondaryView);
        socketConnection.start();
    }

    @Override
    public void postInit(Scene scene) {
        Swatch.GREEN.assignTo(scene);

        scene.getStylesheets().add(GluonApplication.class.getResource("style.css").toExternalForm());
        ((Stage) scene.getWindow()).getIcons().add(new Image(GluonApplication.class.getResourceAsStream("/icon2.png")));


    }
}

Then in the constructor of SocketConnection. You can acces the fields like this:

    /*
     * Constructor
     */
    public SocketConnection(View primaryView, View secondaryView) {

        /*
         * For secondaryView
         */
        statusTextField = (TextField) secondaryView.lookup("#statusTextField");
        ipAddressTextField = (TextField) secondaryView.lookup("#ipAddressTextField");
        startSignalModeComboBox = (ComboBox<String>) secondaryView.lookup("#startSignalModeComboBox");
        predictHorizonTextField = (TextField) secondaryView.lookup("#predictHorizonTextField");
        controlHorizonTextField = (TextField) secondaryView.lookup("#controlHorizonTextField");
        sampleTimeTextField = (TextField) secondaryView.lookup("#sampleTimeTextField");
        pwmDutyCallTextField = (TextField) secondaryView.lookup("#pwmDutyCallTextField");
        endTimeOfStartSignalTextField = (TextField) secondaryView.lookup("#endTimeOfStartSignalTextField");
        referencePointTextField = (TextField) secondaryView.lookup("#referencePointTextField");
        portTextField = (TextField) secondaryView.lookup("#portTextField");

        /*
         * For primaryView
         */
        lineChart = (LineChart<String, Number>) primaryView.lookup("#lineChart");

        /*
         * Declare the data object inside the chart
         */
        time_output = new Series<String, Number>();
        lineChart.getData().add(time_output);


    }
euraad
  • 2,467
  • 5
  • 30
  • 51
  • While this might work for you, it is not the best approach. As suggested in the comments above, you should try other well known solutions mentioned [here](https://stackoverflow.com/questions/14187963/passing-parameters-javafx-fxml). In fact, with the Gluon plugin there is a built in template (check Glisten-Afterburner) that uses Afterburner and DI. Check for instance this [sample](https://github.com/gluonhq/gluon-samples/tree/master/notes) that uses it. In short, create a model and a service, and have your views consume them, but never expose the controls. – José Pereda Mar 10 '19 at 18:03
  • @JoséPereda It seems that that solution requries more work to solve the same problem. – euraad Mar 10 '19 at 18:14
  • For a small project, with one developer, you could do many things, of course, including using static fields that you can expose to everything. That will be the "shortest" way. But that doesn't mean that is the correct way to do it. You asked, we are simply stating what are the usual solutions in this type of projects. You are free to adopt them or not, of course. – José Pereda Mar 10 '19 at 18:24
  • @JoséPereda I think I have already tried this kind of solution and it didn't work for Gluon framework. I have seen that question thread before. If you want, you can post the answer here that can be applied to a Gluon project. – euraad Mar 10 '19 at 19:07