Search code examples
javajavafxtableviewscenebuilderscene

JavaFX - Scene - Modify variable in another scene


I've a scene where I can create a data, now I want to put it into my TableView "TV_currency".

But this one is into another scene who's already open and I don't want to close and reopen this one.

Can you take a look to it ?

ControllerOptions.java (TV_currency is here) :

public class ControllerOptions implements Initializable{
//VARIABLES    
@FXML private   TableView<Currency> TV_currency;
@FXML private   TableColumn<Currency, String> TC_name;
@FXML private   TableColumn<Currency, Double> TC_value;

private ObservableList<Currency> currencies = FXCollections.observableArrayList();

//FUNCTIONS
@Override
public void initialize(URL location, ResourceBundle rb){
    //initialisation Table Currencies
    for (Currency currency : Datas.getInstance().getCurrencies()) {
        currencies.add(currency);
    }       
    TC_name.setCellValueFactory(new PropertyValueFactory<Currency, String>("name"));
    TC_value.setCellValueFactory(new PropertyValueFactory<Currency, Double>("value"));
    TV_currency.setItems(currencies); //TODO Corriger setItems() de la TableView
}

@FXML void add_currency(MouseEvent event) throws IOException {
    FXMLLoader fxmlLoader = new FXMLLoader(getClass().getResource("options/currency_add.fxml"));
    Parent root1 = (Parent) fxmlLoader.load();
    Stage stage = new Stage();
    stage.initModality(Modality.APPLICATION_MODAL);
    stage.initStyle(StageStyle.UNDECORATED);
    stage.setTitle("Add new currency");
    stage.setScene(new Scene(root1));  
    stage.setFullScreen(true);
    stage.show();
}

@FXML void open_options(ActionEvent event) throws IOException {
    Group actor = new Group();
    actor.getChildren().add(FXMLLoader.load(getClass().getResource("options.fxml")));
    com.courieux.wheresmymoney.Main.setScene(actor, "Where's My Money");
}
}

ControllerCurrencyAdd.java (where I want to edit TV_currency):

public class ControllerCurrencyAdd {

@FXML private TextField TF_name;
@FXML private TextField TF_value;
@FXML private TextField TF_acronym;

@FXML
void add(ActionEvent event) {
    Currency currency = new Currency(TF_name.getText(), Double.valueOf(TF_value.getText()));
    Datas.getInstance().addCurrency(currency);

    //==> HERE I NEED TO EDIT VARIABLES BELOW <==
    //currencies.setAll(Datas.getInstance().getCurrencies());
    //TV_currency.setItems(currencies);
}
}

Datas.java :

public class Datas {

//SINGLETON PATTERN
private Datas() {}
private static Datas INSTANCE = new Datas();    
public static Datas getInstance(){
    return INSTANCE;
}
//END SINGLETON PATTERN

//VARS
private List<Currency> currencies = new ArrayList<>();

//FUNCTIONS - Add/Edit/Delete
public void addCurrency(Currency currency){
    currencies.add(currency);
}

//FUNCTIONS - getter/setters
public List<Currency> getCurrencies() {
    return currencies;
}
public void setCurrencies(List<Currency> currencies) {
    this.currencies = currencies;
}
}

Currency.java :

public class Currency {

//VARS
private     String      name;
private     double      value;

//FUNCTIONS - constructors
public Currency(String name, double value) {
    setName(name);
    setValue(value);
}

//FUNCTIONS
public boolean equals(Currency currency){
    if(this.name == currency.getName()
            && this.value == currency.getValue()){
        return true;
    } else {
        return false;
    }
}

//FUNCTIONS - getters/setters
public void setName(String name) {
    this.name = name;
}
public String getName() {
    return name;
}
public void setValue(double value) {
    this.value = value;
}
public double getValue() {
    return value;
}
}

Then I want to put new data into the ObservableList "currencies" and set TV_currency with it.

Thanks in advance !


Solution

  • The simplest change you can make to your code is to let your singleton class hold an ObservableList instead of a List. Then you can use that directly in the table view, and update it directly in other controllers. Since the table view observes its backing list, changes to the observable list will immediately be reflected in the table.

    I.e.

    import javafx.collections.FXCollections;
    import javafx.collections.ObservableList;
    
    public class Datas {
    
        // SINGLETON PATTERN
        private Datas() {
        }
    
        private static Datas INSTANCE = new Datas();
    
        public static Datas getInstance() {
            return INSTANCE;
        }
        // END SINGLETON PATTERN
    
        // VARS
        private ObservableList<Currency> currencies = FXCollections.observableArrayList();
    
        // FUNCTIONS - Add/Edit/Delete
        public void addCurrency(Currency currency) {
            currencies.add(currency);
        }
    
        // FUNCTIONS - getter/setters
        public ObservableList<Currency> getCurrencies() {
            return currencies;
        }
    
    }
    

    and then

    import java.io.IOException;
    import java.net.URL;
    import java.util.ResourceBundle;
    
    import javafx.collections.FXCollections;
    import javafx.collections.ObservableList;
    import javafx.event.ActionEvent;
    import javafx.fxml.FXML;
    import javafx.fxml.FXMLLoader;
    import javafx.fxml.Initializable;
    import javafx.scene.Group;
    import javafx.scene.Parent;
    import javafx.scene.Scene;
    import javafx.scene.control.TableColumn;
    import javafx.scene.control.TableView;
    import javafx.scene.control.cell.PropertyValueFactory;
    import javafx.scene.input.MouseEvent;
    import javafx.stage.Modality;
    import javafx.stage.Stage;
    import javafx.stage.StageStyle;
    
    public class ControllerOptions implements Initializable {
        // VARIABLES
        @FXML
        private TableView<Currency> TV_currency;
        @FXML
        private TableColumn<Currency, String> TC_name;
        @FXML
        private TableColumn<Currency, Double> TC_value;
    
        private ObservableList<Currency> currencies = FXCollections.observableArrayList();
    
        // FUNCTIONS
        @Override
        public void initialize(URL location, ResourceBundle rb) {
            // initialisation Table Currencies
    
            TC_name.setCellValueFactory(new PropertyValueFactory<Currency, String>("name"));
            TC_value.setCellValueFactory(new PropertyValueFactory<Currency, Double>("value"));
    
            // Note how the list from Datas is used directly in the table:
            TV_currency.setItems(Datas.getInstance().getCurrencies()); 
        }
    
        @FXML
        void add_currency(MouseEvent event) throws IOException {
            FXMLLoader fxmlLoader = new FXMLLoader(getClass().getResource("options/currency_add.fxml"));
            Parent root1 = (Parent) fxmlLoader.load();
            Stage stage = new Stage();
            stage.initModality(Modality.APPLICATION_MODAL);
            stage.initStyle(StageStyle.UNDECORATED);
            stage.setTitle("Add new currency");
            stage.setScene(new Scene(root1));
            stage.setFullScreen(true);
            stage.show();
        }
    
        @FXML
        void open_options(ActionEvent event) throws IOException {
            Group actor = new Group();
            actor.getChildren().add(FXMLLoader.load(getClass().getResource("options.fxml")));
            Main.setScene(actor, "Where's My Money");
        }
    }
    

    and

    import javafx.event.ActionEvent;
    import javafx.fxml.FXML;
    import javafx.scene.control.TextField;
    
    public class ControllerCurrencyAdd {
    
        @FXML
        private TextField TF_name;
        @FXML
        private TextField TF_value;
        @FXML
        private TextField TF_acronym;
    
        @FXML
        void add(ActionEvent event) {
            Currency currency = new Currency(TF_name.getText(), Double.valueOf(TF_value.getText()));
    
            // since we update the list used as the table's backing list, the table will automatically update:
            Datas.getInstance().addCurrency(currency);
    
        }
    }
    

    Your Datas class is considered to be a "model" in the MVC (and related) design patterns. Typically making this a singleton can restrict you a bit in terms of modifying the application later (many programmers believe this to be an anti-pattern). You might consider making this a regular class and using dependency-injection techniques to give each controller access to the same instance.