user2785543
user2785543

Reputation: 13

javafx java.lang.NullPointerException as executing event handler function

I am trying to learn some JavaFx these days. I set up a simple MVC and it works well until I click the button to invoke click envet. It throws java.lang.NullPointerException. I think the problem is that the instance variable "controller" is not initialized after GUI launched. But I do initialize it in the main method. Below is view class and what I did in the main method.

package javafxdemogui;

import javafx.application.Application;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.geometry.Pos;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.TextArea;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.HBox;
import javafx.stage.Stage;

/**
 *
 * @author Jason
 */
public class DemoView extends Application {

    private TextArea inputText;
    private TextArea outputText;
    private DemoController controller;

    @Override
    public void start(Stage primaryStage) {
        BorderPane borderPane = new BorderPane();

        this.inputText = new TextArea();
        this.outputText = new TextArea();
        this.inputText.setWrapText(true);
        this.outputText.setWrapText(true);
        this.outputText.setEditable(false);

        borderPane.setTop(inputText);

        HBox hbox = new HBox();
        hbox.setSpacing(10);
        Button resetBtn = new Button("reset");
        Button copyInputBtn = new Button("copyInput");
        hbox.getChildren().addAll(resetBtn, copyInputBtn);
        hbox.setAlignment(Pos.CENTER);
        borderPane.setCenter(hbox);

        borderPane.setBottom(outputText);


        resetBtn.setOnAction(new EventHandler<ActionEvent>() {
            @Override
            public void handle(ActionEvent event) {
                controller.processResetEvent();
            }
        });

        copyInputBtn.setOnAction(new EventHandler<ActionEvent>() {
            @Override
            public void handle(ActionEvent event) {
                controller.processCopyEvent(inputText.getText());
            }
        });

        Scene scene = new Scene(borderPane, 600, 400);
        primaryStage.setTitle("JavaFXDemoGUI");
        primaryStage.setScene(scene);
        primaryStage.show();
    }

    public void registerObserver(DemoController controller) {
        this.controller = controller;
    }

    /**
     * Updates input display based on String provided as argument.
     *
     * @param input new value of input display
     */
    public void updateInputDisplay(String input) {
        this.inputText.setText(input);
    }

    /**
     * Updates output display based on String provided as argument.
     *
     * @param output new value of output display
     */
    public void updateOutputDisplay(String output) {
        this.outputText.setText(output);
    }

    /**
     * The main() method is ignored in correctly deployed JavaFX application.
     * main() serves only as fallback in case the application can not be
     * launched through deployment artifacts, e.g., in IDEs with limited FX
     * support. NetBeans ignores main().
     *
     * @param args the command line arguments
     */
    public void viewLaunch(String[] args) {
        launch(args);
    }
}

And What I did in the main method....

public static void main(String[] args) {
    /*
     * Create instances of the model, view, and controller objects, and
     * initialize them; view needs to know about controller, and controller
     * needs to know about model and view
     */
    DemoModel model = new DemoModel();
    DemoView view = new DemoView();
    DemoController controller = new DemoController(model, view);

    view.registerObserver(controller);
    view.viewLaunch(args);
}

Upvotes: 1

Views: 891

Answers (1)

jewelsea
jewelsea

Reputation: 159321

I advise placing the main() method in your application class and not doing anything in main but launching the application.

I haven't tried it, but I'd be willing to bet that when Application.launch is invoked, that it generates a new instance of your application class, so effectively all of the code you have written in main before the launch is ignored.

I know that for a while, for Java 8 the Oracle team were considering not invoking main on startup for launching a JavaFX application (not sure what the eventual outcome of that was though, perhaps they still invoke the main method).

What you really should do instead is handle all of your initialization in the init or start methods of your application. Also note (in JavaFX 2.2) that if you do stuff in init there are some restrictions on JavaFX objects which can be instantiated (as you are not yet on the JavaFX application thread), for example you can't create Tooltips or WebViews off the JavaFX application thread. For this reason most of the JavaFX applications you see end up creating their UI on the JavaFX application thread at the front of the start method.

Also, a good approach is to shelve any long running tasks which can be done off of the JavaFX application thread (such as reading a database into something like your DemoModel) off to a JavaFX concurrent task, that way you can get progress feedback and messages from that long running task back to your UI to update an initialization status (if your framework requires that level of sophistication).

Upvotes: 1

Related Questions