Использование параметра в методах JavaFX

#java #javafx

#java #javafx

Вопрос:

Я создаю приложение JavaFX и хочу получить доступ к значениям, передаваемым в качестве аргумента в пользовательском интерфейсе JavaFX. По какой-то причине я не могу получить доступ к этим значениям во всех методах, кроме launchForm базового метода. Вот как выглядит мой код.

 public class FormBuilder extends Application {

    /*
     * (non-Javadoc)
     * @see javafx.application.Application#start(javafx.stage.Stage)
     * Scene scene
     *      Group root
     *          BorderPane borderPane
     *              TabPane tabPane
     *                  Tab stocksTab
     *                      BorderPane stockTabBorderPane
     *                          GridPane gridPane
     *          
     */

    private Stocks stockData = new Stocks();
    private int size;

    @Override
    public void start(Stage stage) throws Exception {
        stage.setTitle("Stock Manager");
        Group root = new Group();
        Scene scene = new Scene(root, 1024, 800, Color.WHITE);

        TabPane tabPane = new TabPane();
        BorderPane borderPane = new BorderPane();
        BorderPane stockTabBorderPane = new BorderPane();

        Tab stocksTab = new Tab("Stocks");

        stockTabBorderPane.setTop(this.addHBox());
        stockTabBorderPane.setCenter(this.createGridPane());

        stocksTab.setContent(stockTabBorderPane);
        tabPane.getTabs().add(stocksTab);

        borderPane.setCenter(tabPane);

        borderPane.prefHeightProperty().bind(scene.heightProperty());
        borderPane.prefWidthProperty().bind(scene.widthProperty());

        root.getChildren().add(borderPane);


        stage.setScene(scene);
        stage.show();


    }

    private HBox addHBox() {
        HBox hbox = new HBox();
        hbox.setPadding(new Insets(15, 12, 15, 12));
        hbox.setSpacing(10);
        hbox.setStyle("-fx-background-color: #336699;");

        Button buttonCurrent = new Button("Current");
        buttonCurrent.setPrefSize(100, 20);

        Button buttonProjected = new Button("Projected");
        buttonProjected.setPrefSize(100, 20);
        hbox.getChildren().addAll(buttonCurrent, buttonProjected);

        return hbox;
    }

    private GridPane createGridPane() {

        GridPane gridPane = new GridPane();

        gridPane.setLayoutX(39);
        gridPane.setLayoutY(131.0);
        gridPane.setAlignment(Pos.TOP_CENTER);
        gridPane.setVgap(5.0);
        gridPane.setHgap(10.0);

        gridPane.add(new Label("Active"), 1,1);
        gridPane.add(new Label("Stock"), 2, 1);
        gridPane.add(new Label("Symbol"), 3, 1);
        gridPane.add(new Label("LPP"), 4, 1);
        gridPane.add(new Label("LPP"), 5, 1);
        gridPane.add(new Label("HPP"), 6, 1);
        gridPane.add(new Label("LTP"), 7, 1);

        System.out.println(this.size);
        for(int v=2;v < this.stockData.getStocks().size() 2; v  ) {
            gridPane.add(new CheckBox(), 1, v);
            gridPane.add(new Label("Amazon"), 2, v);
            gridPane.add(new TextField (), 3,v);
            gridPane.add(new TextField (), 4,v);
            gridPane.add(new TextField (), 5,v);
            gridPane.add(new TextField (), 6,v);
            gridPane.add(new TextField (), 7,v);

        }

        return gridPane;
    }

    public void launchForm(Stocks stockData) {
        this.stockData = stockData;
        this.size = stockData.getStocks().size();
        System.out.println(stockData.getStocks().size());
        System.out.println(stockData.getStocks().get(0).getSector());
        launch();
    }
}
  

Теперь проблема в том, что когда я пытаюсь получить доступ к любому значению в stockData объекте внутри createGridPane метода, значения недоступны.

Примерами являются

this.stockData.getStocks().size() дает значение 0 в createGridPane методе. Но это дает значение 2 в launchForm методе.

Опять же, есть и другие значения, такие как

  this.stockData.getStocks().get(0).getSector()
  

который возвращает значение "Retail" в launchForm методе. Но когда я пытаюсь получить доступ к тому же самому в другом методе в том же классе, я получаю исключение.

Может кто-нибудь, пожалуйста, помочь мне здесь?

Комментарии:

1. Откуда вы звоните launchForm ? И почему вы вызываете launch() изнутри этого метода? launch() запуск приложения: конечно, приложение уже запущено в этот момент. Я думаю, вы, должно быть, в корне неправильно поняли жизненный цикл JavaFX: см. Application Документы

2. Я думаю, вам, вероятно, нужно включить в этот вопрос дополнительную информацию о том, что вы делаете, и как вы структурировали свое приложение.

Ответ №1:

Вы вызываете Application.launch метод launchForm экземпляра и ожидаете, что он будет использовать экземпляр, для которого этот метод вызывается, как класс приложения.

Однако запуск JavaFX таким образом не работает.

Если Application.launch вызывается, новый экземпляр класса, из которого вызывается метод, создается самим launch методом, и именно этот новый экземпляр используется с init and start .

Самым простым способом исправить это было бы, если бы вы могли создать Stocks в init or start (возможно, передав некоторые строки в качестве параметров launch ).

В противном случае вам нужен какой-то другой способ связи с вновь созданным экземпляром Application подкласса, например static , members …

Комментарии:

1. Похоже, что это вообще не должно быть Application подклассом, а должно быть Stage подклассом (или должно иметь ссылку на a Stage ). Затем просто замените launch() на show() .

2. @James_D: если операция дошла до того, что start метод вызывается в новом экземпляре, платформа не была запущена, и любая попытка показать Stage неизбежно завершится неудачей…

3. Это правда. Но только кажется, что жизненный цикл здесь неправильный. Очевидно, что перед вызовом выполняется какой-то другой код launchForm . До этого действительно ничего не должно быть; весь код запуска должен быть в init() или start() , за исключением действительно необычных обстоятельств. (Что похоже на то, что вы говорите в своем предпоследнем абзаце.) Таким образом, «другой код» должен быть Application подклассом, а этот класс должен быть чем-то другим. Наверное, я имею в виду, что здесь больше структурных проблем, чем в коде, который опубликовал OP.

4. Таким образом, запись в приложение происходит из класса MyStocks . MyStocks вызывает несколько методов, которые считывают данные из файла json и преобразуют их в java-объект Stocks. В классе MyStocks у меня есть раздел кода, который вызывает метод launchForm и передает объект Stocks в качестве аргумента. FormBuilder FormBuilder = новый FormBuilder(); FormBuilder.launchForm(stockData);

5. @ngnear Просто переместите всю эту функциональность в start() метод. (Или переместите его в отдельный класс и вызовите его из start() метода.) Создайте Application подкласс, в котором у вас есть вход в приложение, и просто вызывайте launch() main() его обычным способом.

Ответ №2:

В JavaFX вы должны в основном рассматривать Application подкласс и, в частности, его start() метод, как точку входа приложения. Жизненный цикл приложения описан в Application Javadocs, но вкратце процесс запуска JavaFX инициируется либо вызовом одного из статических Application.launch(...) методов, либо (с использованием Oracle JDK) запуском JVM и указанием Application подкласса в качестве основного класса (даже если у него нет main метода).

Затем процесс запуска:

  1. запускает инструментарий JavaFX
  2. создает новый экземпляр Application подкласса
  3. вызывает init() Application подкласс (реализация по умолчанию — no-op)
  4. запускает поток приложения FX
  5. вызывает start() Application подкласс, выполняя его в потоке приложения FX.

Несмотря на то, что он вызывается в разных потоках, start() гарантированно не будет вызываться до init() завершения.

Из опубликованного вами кода должно быть так, что вы создаете экземпляр своего FormBuilder класса где-то в другом месте и вызываете launchForm(...) этот экземпляр. Когда вы вызываете launch() оттуда, это создает второй экземпляр и вызывает start() его, как описано выше. Поэтому, конечно, поля, которые вы устанавливаете для экземпляра, для которого launchForm(...) вызывается, не будут установлены для экземпляра, для которого start(...) вызывается.

Вы должны реорганизовать свой код так, чтобы он либо FormBuilder был точкой входа в приложение, либо FormBuilder не был Application подклассом и создал новую точку входа, которая создает и использует ее. Похоже, у вас есть некоторая фоновая работа, которая загружает данные: это должен быть отдельный класс, который не должен быть точкой входа. Итак, первый рефакторинг будет выглядеть так:

 // class that reads data and encapsulates it as a Stocks object

public class StockDataAccessor {

    // ...

    public Stocks getStocks() {
        // ...
    }

}
  

Тогда FormBuilder выглядит так:

 public class FormBuilder extends Application {

    /*
     * (non-Javadoc)
     * @see javafx.application.Application#start(javafx.stage.Stage)
     * Scene scene
     *      Group root
     *          BorderPane borderPane
     *              TabPane tabPane
     *                  Tab stocksTab
     *                      BorderPane stockTabBorderPane
     *                          GridPane gridPane
     *          
     */

    private Stocks stockData ;
    private int size;

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

        StockDataAccessor stockDataAccessor = new StockDataAccessor();
        stockData = stockDataAccessor.getStocks();

        stage.setTitle("Stock Manager");
        Group root = new Group();
        Scene scene = new Scene(root, 1024, 800, Color.WHITE);

        TabPane tabPane = new TabPane();
        BorderPane borderPane = new BorderPane();
        BorderPane stockTabBorderPane = new BorderPane();

        Tab stocksTab = new Tab("Stocks");

        stockTabBorderPane.setTop(this.addHBox());
        stockTabBorderPane.setCenter(this.createGridPane());

        stocksTab.setContent(stockTabBorderPane);
        tabPane.getTabs().add(stocksTab);

        borderPane.setCenter(tabPane);

        borderPane.prefHeightProperty().bind(scene.heightProperty());
        borderPane.prefWidthProperty().bind(scene.widthProperty());

        root.getChildren().add(borderPane);


        stage.setScene(scene);
        stage.show();


    }

    private HBox addHBox() {
        HBox hbox = new HBox();
        hbox.setPadding(new Insets(15, 12, 15, 12));
        hbox.setSpacing(10);
        hbox.setStyle("-fx-background-color: #336699;");

        Button buttonCurrent = new Button("Current");
        buttonCurrent.setPrefSize(100, 20);

        Button buttonProjected = new Button("Projected");
        buttonProjected.setPrefSize(100, 20);
        hbox.getChildren().addAll(buttonCurrent, buttonProjected);

        return hbox;
    }

    private GridPane createGridPane() {

        GridPane gridPane = new GridPane();

        gridPane.setLayoutX(39);
        gridPane.setLayoutY(131.0);
        gridPane.setAlignment(Pos.TOP_CENTER);
        gridPane.setVgap(5.0);
        gridPane.setHgap(10.0);

        gridPane.add(new Label("Active"), 1,1);
        gridPane.add(new Label("Stock"), 2, 1);
        gridPane.add(new Label("Symbol"), 3, 1);
        gridPane.add(new Label("LPP"), 4, 1);
        gridPane.add(new Label("LPP"), 5, 1);
        gridPane.add(new Label("HPP"), 6, 1);
        gridPane.add(new Label("LTP"), 7, 1);

        System.out.println(this.size);
        for(int v=2;v < this.stockData.getStocks().size() 2; v  ) {
            gridPane.add(new CheckBox(), 1, v);
            gridPane.add(new Label("Amazon"), 2, v);
            gridPane.add(new TextField (), 3,v);
            gridPane.add(new TextField (), 4,v);
            gridPane.add(new TextField (), 5,v);
            gridPane.add(new TextField (), 6,v);
            gridPane.add(new TextField (), 7,v);

        }

        return gridPane;
    }

    // for non-JavaFX aware environments (like your IDE...)
    public static void main(String[] args) {
        launch(args);
    }
}
  

Затем запуск FormBuilder в качестве вашего основного класса сделает то, что вам нужно.


Если вы хотите полностью исключить точку входа приложения из FormBuilder класса, альтернативный рефакторинг (который очень похож) выглядит следующим образом:

 public class FormBuilder {

    /*
     * (non-Javadoc)
     * @see javafx.application.Application#start(javafx.stage.Stage)
     * Scene scene
     *      Group root
     *          BorderPane borderPane
     *              TabPane tabPane
     *                  Tab stocksTab
     *                      BorderPane stockTabBorderPane
     *                          GridPane gridPane
     *          
     */

    private Stocks stockData ;
    private int size;

    private Group root ;

    public FormBuilder() {

        StockDataAccessor stockDataAccessor = new StockDataAccessor();
        stockData = stockDataAccessor.getStocks();

        root = new Group();

        TabPane tabPane = new TabPane();
        BorderPane borderPane = new BorderPane();
        BorderPane stockTabBorderPane = new BorderPane();

        Tab stocksTab = new Tab("Stocks");

        stockTabBorderPane.setTop(this.addHBox());
        stockTabBorderPane.setCenter(this.createGridPane());

        stocksTab.setContent(stockTabBorderPane);
        tabPane.getTabs().add(stocksTab);

        borderPane.setCenter(tabPane);

        borderPane.prefHeightProperty().bind(scene.heightProperty());
        borderPane.prefWidthProperty().bind(scene.widthProperty());

        root.getChildren().add(borderPane);



    }

    public Parent getView() {
        return root ;
    }

    private HBox addHBox() {
        HBox hbox = new HBox();
        hbox.setPadding(new Insets(15, 12, 15, 12));
        hbox.setSpacing(10);
        hbox.setStyle("-fx-background-color: #336699;");

        Button buttonCurrent = new Button("Current");
        buttonCurrent.setPrefSize(100, 20);

        Button buttonProjected = new Button("Projected");
        buttonProjected.setPrefSize(100, 20);
        hbox.getChildren().addAll(buttonCurrent, buttonProjected);

        return hbox;
    }

    private GridPane createGridPane() {

        GridPane gridPane = new GridPane();

        gridPane.setLayoutX(39);
        gridPane.setLayoutY(131.0);
        gridPane.setAlignment(Pos.TOP_CENTER);
        gridPane.setVgap(5.0);
        gridPane.setHgap(10.0);

        gridPane.add(new Label("Active"), 1,1);
        gridPane.add(new Label("Stock"), 2, 1);
        gridPane.add(new Label("Symbol"), 3, 1);
        gridPane.add(new Label("LPP"), 4, 1);
        gridPane.add(new Label("LPP"), 5, 1);
        gridPane.add(new Label("HPP"), 6, 1);
        gridPane.add(new Label("LTP"), 7, 1);

        System.out.println(this.size);
        for(int v=2;v < this.stockData.getStocks().size() 2; v  ) {
            gridPane.add(new CheckBox(), 1, v);
            gridPane.add(new Label("Amazon"), 2, v);
            gridPane.add(new TextField (), 3,v);
            gridPane.add(new TextField (), 4,v);
            gridPane.add(new TextField (), 5,v);
            gridPane.add(new TextField (), 6,v);
            gridPane.add(new TextField (), 7,v);

        }

        return gridPane;
    }

}
  

а затем создайте точку входа:

 public class StockApp extends Application {

    @Override
    public void start(Stage stage) {
        FormBuilder formBuilder = new FormBuilder();
        Scene scene = new Scene(formBuilder.getView(), 1024, 800, Color.WHITE);
        stage.setTitle("Stock Manager");
        stage.setScene(scene);
        stage.show();
    }

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

Комментарии:

1. Это помогло, и я смог устранить проблему. Спасибо.