3 Replies Latest reply: Jun 6, 2013 7:16 AM by James_D RSS

    Create floating window

    1012709
      Can you tell me how can i create floating window in java FX. At the moment i creating new form with new Stage and new Scene which is set to stage. But i have a problem with memory because if i closed stage "stage.close()" so i can't destroy scene and garbage collector cannot clear scene from memory. How can i do this? How can i create floating window which if destroy so window and all links on this component will be released from memory.

      Can I creating floating windows by dynamic code in class files?
        • 1. Re: Create floating window
          James_D
          This shouldn't be a problem. Stages and Scenes behave just as any other Java object: once you have no live references to them they are eligible for garbage collection. What's your evidence that the gc cannot remove the scene from memory?
          • 2. Re: Create floating window
            1012709
            I find that class TextField and TextArea has strong link on own scene. And when we destroy floating window "stage.close()" so textfiel can not remove from memmory because strong link.

            I can't destroy stage or scene and garbage collector cannot release scene from memory. Graphic commponent is hided but it is not destroyed.
            • 3. Re: Create floating window
              James_D
              1009706 wrote:
              I find that class TextField and TextArea has strong link on own scene. And when we destroy floating window "stage.close()" so textfiel can not remove from memmory because strong link.
              I think by this you mean that your main application screen has a reference to a TextField and TextArea displayed in the stage you want to close?

              As I understand it, each node retains a reference to its parent, and the root node (and maybe others) retains a reference to its Scene. So by keeping a reference to Nodes in the UI for your dialog, you keep the entire scene graph for the dialog (and possibly the dialog itself) in scope.
              I can't destroy stage or scene and garbage collector cannot release scene from memory. Graphic commponent is hided but it is not destroyed.
              Correct, because you've kept live references to it in scope.

              The short answer is "don't do it like this". It's usually a bad OO design to share elements of a user interface outside of the place where they are defined. Instead, share the data to the dialog and have the dialog communicate with the data.

              In short, "Share the data, not the user interface".

              Here's an example, using the usual Person table. Notice how I pass a reference to the data (the list of Persons and a Person to edit) to the class that displays the dialog, but I don't expose anything from the dialog at all to the main application code. This not only helps avoid memory leaks, but it means it's much easier to redesign the user interface for the dialog without having to update any other code. You could also use the same basic technique and use FXML for the dialog; just share the data with the controller. Even if you have a more complex interaction between the main application and the dialog, you can always find a way to structure it like this.
              import javafx.application.Application;
              import javafx.beans.binding.Bindings;
              import javafx.beans.property.SimpleStringProperty;
              import javafx.beans.property.StringProperty;
              import javafx.collections.FXCollections;
              import javafx.collections.ObservableList;
              import javafx.event.ActionEvent;
              import javafx.event.EventHandler;
              import javafx.geometry.HPos;
              import javafx.scene.Node;
              import javafx.scene.Scene;
              import javafx.scene.control.Button;
              import javafx.scene.control.Label;
              import javafx.scene.control.TableColumn;
              import javafx.scene.control.TableView;
              import javafx.scene.control.TextField;
              import javafx.scene.control.cell.PropertyValueFactory;
              import javafx.scene.layout.BorderPane;
              import javafx.scene.layout.ColumnConstraints;
              import javafx.scene.layout.GridPane;
              import javafx.scene.layout.HBox;
              import javafx.scene.layout.Pane;
              import javafx.scene.layout.Priority;
              import javafx.scene.layout.VBox;
              import javafx.stage.Stage;
              import javafx.stage.StageStyle;
              import javafx.stage.Window;
              
              public class TableWithEditDialog extends Application {
              
                @Override
                public void start(final Stage primaryStage) {
                  final BorderPane root = new BorderPane();
                  final TableView<Person> table = new TableView<Person>();
                  table.setItems(createData());
                  final TableColumn<Person, String> firstNameColumn = new TableColumn<>("First Name");
                  final TableColumn<Person, String> lastNameColumn = new TableColumn<>("Last Name");
                  firstNameColumn.setCellValueFactory(new PropertyValueFactory<Person, String>("firstName"));
                  lastNameColumn.setCellValueFactory(new PropertyValueFactory<Person, String>("lastName"));
                  table.getColumns().addAll(firstNameColumn, lastNameColumn);
                  
                  HBox controls = new HBox(5);
                  Button addButton = new Button("Add..");
                  Button editButton = new Button("Edit...");
                  Button removeButton = new Button("Remove");
                  editButton.disableProperty().bind(
                      Bindings.size(table.getSelectionModel().getSelectedItems()).isNotEqualTo(1));
                  removeButton.disableProperty().bind(
                      Bindings.isEmpty(table.getSelectionModel().getSelectedItems()));
                  
                  addButton.setOnAction(new EventHandler<ActionEvent>() {
                    @Override
                    public void handle(ActionEvent event) {
                      Pane editor = new PersonEditor(table.getItems(), null).createView();
                      Scene scene = new Scene(editor, 400, 200);
                      Stage dialog = new Stage();
                      dialog.setScene(scene);
                      dialog.initStyle(StageStyle.UTILITY);
                      dialog.initOwner(primaryStage);
                      dialog.show();
                    }
                  });
                  
                  editButton.setOnAction(new EventHandler<ActionEvent>() {
                    @Override
                    public void handle(ActionEvent event) {
                      Pane editor = new PersonEditor(table.getItems(), table.getSelectionModel().getSelectedItem()).createView();
                      Scene scene = new Scene(editor, 400, 200);
                      Stage dialog = new Stage();
                      dialog.setScene(scene);
                      dialog.initStyle(StageStyle.UTILITY);
                      dialog.initOwner(primaryStage);
                      dialog.show();        
                    }
                  });
                  
                  removeButton.setOnAction(new EventHandler<ActionEvent>() {
                    @Override
                    public void handle(ActionEvent event) {
                      table.getItems().removeAll(table.getSelectionModel().getSelectedItems());
                    }
                  });
                  controls.getChildren().addAll(addButton, editButton, removeButton);
                  
                  root.setTop(controls);
                  root.setCenter(table);
                  primaryStage.setScene(new Scene(root, 400, 600));
                  primaryStage.show();    
                }
              
                public static void main(String[] args) {
                  launch(args);
                }
                
                private ObservableList<Person> createData() {
                  return FXCollections.observableArrayList(
                      new Person("Hugo" ,"Lloris"),
                      new Person("Benoit", "Assou-Ekotto"),
                      new Person("Jan", "Vertonghen"),
                      new Person("Michael", "Dawson"),
                      new Person("Kyle", "Walker"),
                      new Person("Scott", "Parker"),
                      new Person("Mousa", "Dembele"),
                      new Person("Gylfi","Sigurdsson"),
                      new Person("Gareth", "Bale"),
                      new Person("Aaron", "Lennon"),
                      new Person("Jermaine", "Defoe")
                  );
                }
                
                public static class Person {
                  private final StringProperty firstName ;
                  private final StringProperty lastName ;
                  Person(String firstName, String lastName) {
                    this.firstName = new SimpleStringProperty(this, "firstName", firstName);
                    this.lastName = new SimpleStringProperty(this, "lastName", lastName);
                  }
                  public String getFirstName() { return firstName.get(); }
                  public void setFirstName(String firstName) { this.firstName.set(firstName);}
                  public StringProperty firstNameProperty() { return firstName ; }
                  public String getLastName() { return lastName.get(); }
                  public void setLastName(String lastName) { this.lastName.set(lastName); }
                  public StringProperty lastNameProperty() { return lastName ; }    
                  @Override public String toString() { return firstName.get() + " " + lastName.get() ; }
                  
                }
                
                class PersonEditor {
                  final ObservableList<Person> personList ;
                  Person personToEdit ;
                  PersonEditor(ObservableList<Person> personList, Person personToEdit) {
                    this.personList = personList ;
                    this.personToEdit = personToEdit ;
                  }
                  Pane createView() {
                    final VBox root = new VBox(5);
                    final GridPane grid = new GridPane();
                    final TextField firstNameTF = new TextField();
                    final TextField lastNameTF = new TextField();
                    if (personToEdit != null) {
                      firstNameTF.setText(personToEdit.getFirstName());
                      lastNameTF.setText(personToEdit.getLastName());
                    }
                    grid.addRow(0, new Label("First Name:"), firstNameTF);
                    grid.addRow(1, new Label("Last Name:"), lastNameTF);
                    final ColumnConstraints leftColConstraints = new ColumnConstraints();
                    leftColConstraints.setHalignment(HPos.RIGHT);
                    leftColConstraints.setHgrow(Priority.NEVER);
                    final ColumnConstraints rightColConstraints = new ColumnConstraints();
                    leftColConstraints.setHalignment(HPos.LEFT);
                    leftColConstraints.setHgrow(Priority.ALWAYS);
                    grid.getColumnConstraints().add(0, leftColConstraints);
                    grid.getColumnConstraints().add(0, rightColConstraints);
                    
                    final HBox buttons = new HBox(5);
                    final Button okButton = new Button("OK");
                    final Button cancelButton = new Button("Cancel");
                    okButton.setOnAction(new EventHandler<ActionEvent>() {
                      @Override
                      public void handle(ActionEvent event) {
                        if (personToEdit == null) {
                          personToEdit = new Person("","");
                          personList.add(personToEdit);
                        }
                        personToEdit.setFirstName(firstNameTF.getText());
                        personToEdit.setLastName(lastNameTF.getText());
                        closeStage(root);
                      }
                    });
                    cancelButton.setOnAction(new EventHandler<ActionEvent>() {
                      @Override
                      public void handle(ActionEvent event) {
                        closeStage(root);
                      }
                    });
                    buttons.getChildren().addAll(okButton, cancelButton);
                    root.getChildren().addAll(grid, buttons);
                    return root ;
                  }
                  private void closeStage(Node node) {
                    Scene scene = node.getScene();
                    if (scene != null) {
                      Window window = scene.getWindow();
                      if (window != null) {
                        window.hide();
                      }
                    }
                  }
                }
              }