I want to seperate a JavaFX project to model, view and controller.
I use netbeans when creating a JavaFX application.
But I want the code seperate, an own GUI, own logic and a Main class just to start the application (I want 3 seperate classes).
But I am not able to solve this problem.
The automatic created code looks like this:
package at.wueschn.www;
import javafx.application.Application;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.layout.StackPane;
import javafx.stage.Stage;
public class Main extends Application {
@Override
public void start(Stage primaryStage) {
Button btn = new Button();
btn.setText("Say 'Hello World'");
btn.setOnAction(new EventHandler<ActionEvent>() {
@Override
public void handle(ActionEvent event) {
System.out.println("Hello World!");
}
});
StackPane root = new StackPane();
root.getChildren().add(btn);
Scene scene = new Scene(root, 300, 250);
primaryStage.setTitle("Hello World!");
primaryStage.setScene(scene);
primaryStage.show();
}
public static void main(String[] args) {
launch(args);
}
}
If you are using NetBeans
, first choose File -> New Project
. Then JavaFX -> JavaFX FXML Application
Note: This is a basic MVC
setup. You could do all of this using pure code. James_D could probably help you with more advanced MCV
ideas.
Note: If you are going to take this simple approach, I suggest you download SceneBuilder
to help you with the view.
Tutorial
Here is a "Java-only" (i.e. no FXML) example of MVC. Note that there are many different variants of MVC, which is a very loosely-defined pattern. This is a kind of "classical" variant: the model has no knowledge of the view(s) or controller(s) (which is the common theme to all MVC-type designs), the controller has a reference to the model and invokes methods on it, implementing some simple logic, and the view has a reference to both the model and controller; observing the model and updating the view components when the data changes, and invoking methods on the controller in response to user input. Other variants of this pattern (MVVM, MVP, etc) typically vary in the relationship between the view and the controller.
This simple application implements a very basic calculator, which simply knows how to add two single-digit integers.
The model:
import javafx.beans.binding.NumberBinding;
import javafx.beans.property.IntegerProperty;
import javafx.beans.property.SimpleIntegerProperty;
public class Model {
private boolean firstNumberEntered ;
private final IntegerProperty firstNumber = new SimpleIntegerProperty();
private final IntegerProperty secondNumber = new SimpleIntegerProperty();
private final NumberBinding sum = firstNumber.add(secondNumber);
public Model() {
firstNumber.addListener((obs, oldValue, newValue) -> firstNumberEntered = true );
}
public IntegerProperty firstNumberProperty() {
return firstNumber ;
}
public int getFirstNumber() {
return firstNumberProperty().get();
}
public void setFirstNumber(int number) {
firstNumberProperty().set(number);
}
public IntegerProperty secondNumberProperty() {
return secondNumber ;
}
public int getSecondNumber() {
return secondNumberProperty().get();
}
public void setSecondNumber(int number) {
secondNumberProperty().set(number);
}
public NumberBinding sumBinding() {
return sum ;
}
public int getSum() {
return sum.intValue();
}
public boolean isFirstNumberEntered() {
return firstNumberEntered ;
}
public void reset() {
setFirstNumber(0);
setSecondNumber(0);
firstNumberEntered = false ;
}
}
The controller:
public class Controller {
private final Model model ;
public Controller(Model model) {
this.model = model ;
}
public void enterFirstNumber(int number) {
model.setFirstNumber(number);
}
public void enterSecondNumber(int number) {
model.setSecondNumber(number);
}
public void clear() {
model.reset();
}
public void enterNumber(int number) {
if (model.isFirstNumberEntered()) {
enterSecondNumber(number) ;
} else {
enterFirstNumber(number);
}
}
}
The view:
import javafx.beans.binding.Bindings;
import javafx.geometry.Insets;
import javafx.geometry.Pos;
import javafx.scene.Parent;
import javafx.scene.control.Button;
import javafx.scene.control.Label;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.ColumnConstraints;
import javafx.scene.layout.GridPane;
import javafx.scene.layout.RowConstraints;
public class View {
private final BorderPane root ;
private final Controller controller ;
public View(Model model, Controller controller) {
this.controller = controller ;
root = new BorderPane();
GridPane buttons = new GridPane();
configureButtons(buttons);
createAndAddButtons(controller, buttons);
Label resultLabel = new Label();
configureDisplay(model, resultLabel);
root.setTop(resultLabel);
root.setCenter(buttons);
root.setStyle("-fx-font-size: 36pt;");
}
private void configureDisplay(Model model, Label resultLabel) {
BorderPane.setAlignment(resultLabel, Pos.CENTER_RIGHT);
BorderPane.setMargin(resultLabel, new Insets(5));
resultLabel.textProperty().bind(Bindings.createStringBinding(
() -> String.format("%d + %d = %d", model.getFirstNumber(), model.getSecondNumber(), model.getSum()),
model.firstNumberProperty(), model.secondNumberProperty(), model.sumBinding()));
}
private void createAndAddButtons(Controller controller, GridPane buttons) {
for (int i = 1 ; i <= 9 ; i++) {
int row = (9 - i) / 3 ;
int column = (i -1) % 3 ;
buttons.add(createNumberButton(i), column, row);
}
buttons.add(createNumberButton(0), 0, 3);
Button clearButton = createButton("C");
clearButton.setOnAction(e -> controller.clear());
buttons.add(clearButton, 1, 3, 2, 1);
}
private void configureButtons(GridPane buttons) {
for (int row = 0 ; row < 4 ; row++) {
RowConstraints rc = new RowConstraints();
rc.setFillHeight(true);
rc.setPercentHeight(100.0 / 4);
buttons.getRowConstraints().add(rc);
}
for (int column = 0 ; column < 3 ; column++) {
ColumnConstraints cc = new ColumnConstraints();
cc.setFillWidth(true);
cc.setPercentWidth(100.0 / 3);
buttons.getColumnConstraints().add(cc);
}
buttons.setVgap(5);
buttons.setHgap(5);
buttons.setPadding(new Insets(5));
}
public Parent getRoot() {
return root ;
}
private Button createNumberButton(int number) {
Button button = createButton(Integer.toString(number));
button.setOnAction(e -> controller.enterNumber(number));
return button ;
}
private Button createButton(String text) {
Button button = new Button(text);
button.setMaxSize(Double.MAX_VALUE, Double.MAX_VALUE);
return button ;
}
}
and finally the "main" class which creates each piece and displays the view in a window:
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.stage.Stage;
public class TrivialCalcaulatorApp extends Application {
@Override
public void start(Stage primaryStage) {
Model model = new Model();
Controller controller = new Controller(model);
View view = new View(model, controller);
Scene scene = new Scene(view.getRoot());
primaryStage.setScene(scene);
primaryStage.show();
}
public static void main(String[] args) {
launch(args);
}
}