Issue
How can I put the GridPane named "sellingPane" in a separate class file but still use it in this class? I want to be able to change the GridPane for a different Gridpane in the scene. I want to understand how I can put Nodes in different class files and put them together to create a scene. I tried to put sellingPane in a different class and used "return sellingPane" to return the GridPane to MainWindowView.java, but I don't know how I can make the buttons setOnAction functions work. A small program as an example would be very helpful.
import javafx.beans.property.SimpleObjectProperty;
import javafx.collections.ObservableList;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.scene.control.*;
import javafx.scene.control.cell.PropertyValueFactory;
import javafx.scene.layout.GridPane;
import javafx.scene.layout.HBox;
import javafx.scene.layout.VBox;
import javafx.scene.paint.Color;
import javafx.scene.text.Font;
import nl.inholland.cinemamarkkea.controllers.MovieController;
import nl.inholland.cinemamarkkea.models.Movie;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.Optional;
public class MainWindowView extends HBox {
private ObservableList<Movie> room1Movies;
private ObservableList<Movie> room2Movies;
private Movie movie;
public MainWindowView(MovieController movieController){
Label title = new Label("Purchase tickets");
title.setFont(new Font("Arial", 24));
title.setTextFill(Color.web("#286DA3"));
room1Movies = movieController.getRoom1Movies();
room2Movies = movieController.getRoom2Movies();
//Tableview for the room1Movies
Label room1Label = new Label("Room 1");
room1Label.setFont(new Font("Arial", 12));
TableView<Movie> room1TableView = new TableView<>();
room1TableView.setMinWidth(450);
TableColumn<Movie, LocalDateTime> start1Column = new TableColumn<>("Start");
start1Column.setCellValueFactory(Movie -> {
SimpleObjectProperty startLabel= new SimpleObjectProperty();
DateTimeFormatter format = DateTimeFormatter.ofPattern("dd-MM-yyyy HH:mm");
startLabel.setValue(Movie.getValue().getStartDateTime().format(format));
return startLabel;
});
TableColumn<Movie, LocalDateTime> end1Column = new TableColumn<>("End");
end1Column.setCellValueFactory(Movie -> {
SimpleObjectProperty endLabel= new SimpleObjectProperty();
DateTimeFormatter format = DateTimeFormatter.ofPattern("dd-MM-yyyy HH:mm");
endLabel.setValue(Movie.getValue().getEndDateTime().format(format));
return endLabel;
});
TableColumn<Movie, String> title1Column = new TableColumn<>("Title");
title1Column.setCellValueFactory(new PropertyValueFactory<>("title"));
TableColumn<Movie, Integer> seats1Column = new TableColumn<>("Seats");
seats1Column.setCellValueFactory(new PropertyValueFactory<>("seats"));
TableColumn<Movie, Double> price1Column = new TableColumn<>("Price");
price1Column.setCellValueFactory(new PropertyValueFactory<>("price"));
room1TableView.getColumns().addAll(start1Column, end1Column, title1Column, seats1Column, price1Column);
room1TableView.setItems(room1Movies);
//Tableview for the room2Movies
Label room2Label = new Label("Room 2");
room2Label.setFont(new Font("Arial", 12));
TableView<Movie> room2TableView = new TableView<>();
room2TableView.setMinWidth(450);
TableColumn<Movie, LocalDateTime> start2Column = new TableColumn<>("Start");
start2Column.setCellValueFactory(Movie -> {
SimpleObjectProperty startLabel= new SimpleObjectProperty();
DateTimeFormatter format = DateTimeFormatter.ofPattern("dd-MM-yyyy HH:mm");
startLabel.setValue(Movie.getValue().getStartDateTime().format(format));
return startLabel;
});
TableColumn<Movie, LocalDateTime> end2Column = new TableColumn<>("End");
end2Column.setCellValueFactory(Movie -> {
SimpleObjectProperty endLabel= new SimpleObjectProperty();
DateTimeFormatter format = DateTimeFormatter.ofPattern("dd-MM-yyyy HH:mm");
endLabel.setValue(Movie.getValue().getEndDateTime().format(format));
return endLabel;
});
TableColumn<Movie, String> title2Column = new TableColumn<>("Title");
title2Column.setCellValueFactory(new PropertyValueFactory<>("title"));
TableColumn<Movie, Integer> seats2Column = new TableColumn<>("Seats");
seats2Column.setCellValueFactory(new PropertyValueFactory<>("seats"));
TableColumn<Movie, Double> price2Column = new TableColumn<>("Price");
price2Column.setCellValueFactory(new PropertyValueFactory<>("price"));
room2TableView.getColumns().addAll(start2Column, end2Column, title2Column, seats2Column, price2Column);
room2TableView.setItems(room2Movies);
Label roomLabel = new Label("Room");
Label roomNameLabel = new Label("");
Label movieLabel = new Label("Movie title");
Label movieNameLabel = new Label();
Label startLabel = new Label("Start");
Label startTimeLabel = new Label();
Label seatsLabel = new Label("No. of seats");
ComboBox<Integer> seatsBox = new ComboBox<Integer>();
seatsBox.getItems().add(0);
seatsBox.getItems().add(1);
seatsBox.getItems().add(2);
Button purchaseButton = new Button("Purchase");
Label endLabel = new Label("End");
Label endTimeLabel = new Label();
Label nameLabel = new Label("Name");
TextField nameInput = new TextField();
nameInput.setMaxWidth(150);
Button clearButton = new Button("Clear");
//selling tickets pane
GridPane sellingPane = new GridPane();
sellingPane.add(roomLabel, 0, 0, 1, 1);
sellingPane.add(roomNameLabel, 1, 0, 1, 1);
sellingPane.add(movieLabel, 2, 0, 1, 1);
sellingPane.add(movieNameLabel, 3, 0, 1, 1);
sellingPane.add(startLabel, 0, 1, 1, 1);
sellingPane.add(startTimeLabel, 1, 1, 1, 1);
sellingPane.add(seatsLabel, 2, 1, 1, 1);
sellingPane.add(seatsBox, 3, 1, 1, 1);
sellingPane.add(purchaseButton, 4, 1, 1, 1);
sellingPane.add(endLabel, 0, 2, 1, 1);
sellingPane.add(endTimeLabel, 1, 2, 1, 1);
sellingPane.add(nameLabel, 2, 2, 1, 1);
sellingPane.add(nameInput, 3, 2, 1, 1);
sellingPane.add(clearButton, 4,2,1,1);
//hide pane when no movie is selected
sellingPane.setVisible(false);
sellingPane.setVgap(10);
sellingPane.setHgap(40);
sellingPane.setStyle("-fx-padding: 10;" + "-fx-border-width: 2;");
room1TableView.getSelectionModel().selectedItemProperty().addListener((obs, oldSelection, newSelection) ->{
movie = newSelection;
if (newSelection != null){
sellingPane.setVisible(true);
roomNameLabel.setText("Room 1");
movieNameLabel.setText(newSelection.getTitle());
DateTimeFormatter format = DateTimeFormatter.ofPattern("dd-MM-yyyy HH:mm");
startTimeLabel.setText(newSelection.getStartDateTime().format(format));
endTimeLabel.setText(newSelection.getEndDateTime().format(format));
}
});
room2TableView.getSelectionModel().selectedItemProperty().addListener((obs, oldSelection, newSelection) ->{
movie = newSelection;
if (newSelection != null){
sellingPane.setVisible(true);
roomNameLabel.setText("Room 2");
movieNameLabel.setText(newSelection.getTitle());
DateTimeFormatter format = DateTimeFormatter.ofPattern("dd-MM-yyyy HH:mm");
startTimeLabel.setText(newSelection.getStartDateTime().format(format));
endTimeLabel.setText(newSelection.getEndDateTime().format(format));
}
});
Label errorLabel = new Label();
clearButton.setOnAction(new EventHandler<ActionEvent>() {
@Override
public void handle(ActionEvent actionEvent) {
//hide form
sellingPane.setVisible(false);
//clear combobox
seatsBox.valueProperty().set(null);
//clear name input field
nameInput.clear();
//clear error message
errorLabel.setText("");
//unselect row in table
room1TableView.getSelectionModel().select(null);
room2TableView.getSelectionModel().select(null);
//clear movie
movie = null;
}
});
purchaseButton.setOnAction(new EventHandler<ActionEvent>() {
@Override
public void handle(ActionEvent actionEvent) {
Integer selectedSeats = seatsBox.getValue();
String name = nameInput.getText();
if (selectedSeats == null || selectedSeats == 0){
errorLabel.setText("The minimum number of tickets you need to buy 1.");
} else if (name == null) {
errorLabel.setText("Please fill in your name.");
} else if (movie.getSeats() == 0) {
errorLabel.setText("There are no seats left for this movie, please try different room.");
} else {
//clear error message from possible previous attempt
errorLabel.setText("");
Alert alert = new Alert(Alert.AlertType.CONFIRMATION);
alert.setTitle("Confirm payment");
alert.setHeaderText("Please confirm payment");
alert.setContentText("Do you want to buy tickets for this movie?");
Optional<ButtonType> result = alert.showAndWait();
if (result.get() == ButtonType.OK){
movieController.purchaseTicket(movie, selectedSeats, name, roomNameLabel.getText());
room1TableView.refresh();
room2TableView.refresh();
//clear and hide form
clearButton.fire();
}
}
}
});
VBox mainPane = new VBox();
//rooms together
HBox roomsPane = new HBox();
//rooms
VBox room1Pane = new VBox();
VBox room2Pane = new VBox();
roomsPane.setStyle("-fx-padding: 10;" +
"-fx-border-style: solid inside;" +
"-fx-border-width: 2;" +
"-fx-border-color: #8398AE;");
roomsPane.setSpacing(10);
room2Pane.getChildren().addAll(room2Label, room2TableView);
room1Pane.getChildren().addAll(room1Label, room1TableView);
roomsPane.getChildren().addAll(room1Pane, room2Pane);
mainPane.getChildren().addAll(title, roomsPane, sellingPane, errorLabel);
this.getChildren().addAll(mainPane);
}
}
Solution
You need to work on separating your views from your controllers. Essentially you need to work on Model View Controller (MVC). Your controller should handle events and such. For example, instead of using an anonymous class here
purchaseButton.setOnAction(new EventHandler<ActionEvent>() {
Create a method in your controller class.
public class SellingPaneController{
Node sellingPane;
Button purchaseButton;
ComboBox<Integer> seatsBox;
public SellingPaneController(){
sellingPane = ...;
purchaseButton = ...;
purchaseButton.setOnAction(this::purchaseButtonAction);
}
public void purchaseButtonAction( ActionEvent actionEvent ){
//All of you local GUI classes here should be part of the grid view.
}
public Node getSellingPane(){ return sellingPane;}
}
When you create the controller it will build the GUI elements and attach the listeners.
Note: This is an advantage of FXML, it will build the gui components and attach the listeners for you. You just declare all of the fields and write the event handlers.
It is quite a bit of work because you have tied everything together. For example room1Movies
is part of the MovieController. You might do something like make your SellingPaneController contructor take a MovieController for an argument, then it can keep a reference.
Answered By - matt
Answer Checked By - Katrina (JavaFixing Volunteer)