JavaFX: 2 independent windows at once

后端 未结 2 555
孤独总比滥情好
孤独总比滥情好 2021-01-25 12:01

I\'d like to create 2 independent windows at once. One window would be able to hold an observable list, the other one would show the selected listobject\'s properties. I\'m tryi

相关标签:
2条回答
  • 2021-01-25 12:49

    You basically have to follow @Slaw instructions. Create a Model. Share the Model between the two Controllers. Observe the model's current Customer and react accordingly. MCVE Below:

    Main Class: (load both stages with the correct Scene. Create model and pass it to both Controllers):

    import java.io.IOException;
    import java.util.logging.Level;
    import java.util.logging.Logger;
    import javafx.application.Application;
    import javafx.fxml.FXMLLoader;
    import javafx.scene.Parent;
    import javafx.scene.Scene;
    import javafx.stage.Stage;
    
    /**
     *
     * @author sedri
     */
    public class JavaFXApplication36 extends Application {
    
        @Override
        public void start(Stage stage) {
            try {
                FXMLLoader listViewFXMLLoader = new FXMLLoader(getClass().getResource("ListViewFXML.fxml"));
                Parent listViewRoot = listViewFXMLLoader.load();
                ListViewController listViewController = listViewFXMLLoader.getController();
                Scene scene1 = new Scene(listViewRoot);
                stage.setScene(scene1);           
    
                FXMLLoader detailsFXMLLoader = new FXMLLoader(getClass().getResource("DetailsFXML.fxml"));
                Parent detailsRoot = detailsFXMLLoader.load();
                DetailsController detailsController = detailsFXMLLoader.getController();
                Scene scene2 = new Scene(detailsRoot);
                Stage stage2 = new Stage();
                stage2.setScene(scene2);
    
                DataModel model = new DataModel();
                listViewController.initModel(model);
                detailsController.initModel(model);
    
                stage.show();
                stage2.show(); 
            } catch (IOException ex) {
                Logger.getLogger(JavaFXApplication36.class.getName()).log(Level.SEVERE, null, ex);
            }
        }
    
        /**
         * @param args the command line arguments
         */
        public static void main(String[] args) {
            launch(args);
        }
    
    }
    

    Model Class: (Keep up with current Customer and ObservableList of Customer)

    import javafx.beans.Observable;
    import javafx.beans.property.ObjectProperty;
    import javafx.beans.property.SimpleObjectProperty;
    import javafx.collections.FXCollections;
    import javafx.collections.ObservableList;
    
    /**
     *
     * @author sedrick
     */
    public class DataModel {
        private final ObservableList<Customer> customerList = FXCollections.observableArrayList(customer -> new Observable[]{customer.nameProperty(), customer.ageProperty()});
        private final ObjectProperty<Customer> currentCustomer = new SimpleObjectProperty();
    
        public ObjectProperty<Customer> currentCustomerProperty() {
            return currentCustomer;
        }
    
        public void setCurrentCustomer(Customer currentCustomer) {
            this.currentCustomer.set(currentCustomer);
        }
    
        public Customer getCurrentCustomer() {
            return this.currentCustomer.get();
        }  
    
    
        public ObservableList<Customer> loadCustomers()
        {        
            customerList.add(new Customer("John Doe", 21));
            customerList.add(new Customer("Jane Joe", 20));
    
            return customerList;
        }
    }
    

    Customer Class:

    import javafx.beans.property.IntegerProperty;
    import javafx.beans.property.SimpleIntegerProperty;
    import javafx.beans.property.SimpleStringProperty;
    import javafx.beans.property.StringProperty;
    /**
     *
     * @author sedrick
     */
    public class Customer {
        private final StringProperty name = new SimpleStringProperty();
        private final IntegerProperty age = new SimpleIntegerProperty();
        public Customer(String name, int age) {
            this.name.set(name);
            this.age.set(age);
        }
    
        public String getName()
        {
           return this.name.get();
        }
    
        public void setName(String name)
        {
            this.name.set(name);
        }
    
        public StringProperty nameProperty()
        {
            return this.name;
        }
    
        public int getAge()
        {
           return this.age.get();
        }
    
        public void setAge(int age)
        {
            this.age.set(age);
        }
    
        public IntegerProperty ageProperty()
        {
            return this.age;
        }
    }
    

    ListView Controller: (Initialize model, setup ListView and observe current customer property)

    import java.net.URL;
    import java.util.ResourceBundle;
    import javafx.fxml.FXML;
    import javafx.fxml.Initializable;
    import javafx.scene.control.ListCell;
    import javafx.scene.control.ListView;
    
    /**
     *
     * @author sedri
     */
    public class ListViewController implements Initializable {
       @FXML private ListView<Customer> listView;
    
       private DataModel model;
    
    
    
        @Override
        public void initialize(URL url, ResourceBundle rb) {
            // TODO
        }    
    
        public void initModel(DataModel model)
        {
            // ensure model is only set once:
            if (this.model != null) {
                throw new IllegalStateException("Model can only be initialized once");
            }
    
            listView.getSelectionModel().selectedItemProperty().addListener((obs, oldCustomer, newCustomer) -> 
                model.setCurrentCustomer(newCustomer));
    
            model.currentCustomerProperty().addListener((obs, oldCustomer, newCustomer) -> {
                if (newCustomer == null) {
                    listView.getSelectionModel().clearSelection();
                } else {
                    listView.getSelectionModel().select(newCustomer);
                }
            });
    
            listView.setCellFactory(lv -> new ListCell<Customer>() {
                @Override
                public void updateItem(Customer customer, boolean empty) {
                    super.updateItem(customer, empty);
                    if (empty) {
                        setText(null);
                    } else {
                        setText("Name: " + customer.getName() +  "  Age: " + customer.getAge());
                    }
                }
            });
    
            listView.setItems(model.loadCustomers());
        }
    }
    

    ListView FXML:

    <StackPane maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" prefHeight="400.0" prefWidth="600.0" xmlns="http://javafx.com/javafx/8.0.171" xmlns:fx="http://javafx.com/fxml/1" fx:controller="javafxapplication36.ListViewController">
       <children>
          <ListView fx:id="listView" prefHeight="200.0" prefWidth="200.0" />
       </children>
    </StackPane>
    

    DetailsController:(Initialize model and observe current customer property)

    import java.net.URL;
    import java.util.ResourceBundle;
    import javafx.fxml.FXML;
    import javafx.fxml.Initializable;
    import javafx.scene.control.TextField;
    
    /**
     * FXML Controller class
     *
     * @author sedri
     */
    public class DetailsController implements Initializable {
        @FXML TextField tfName, tfAge;
    
        private DataModel model;
        /**
         * Initializes the controller class.
         */
        @Override
        public void initialize(URL url, ResourceBundle rb) {
            // TODO
        }    
    
         public void initModel(DataModel model) {
            // ensure model is only set once:
            if (this.model != null) {
                throw new IllegalStateException("Model can only be initialized once");
            }
    
            this.model = model ;
    
            model.currentCustomerProperty().addListener((observable, oldCustomer, newCustomer) -> {
                if(newCustomer == null){
                    tfName.setText("");
                    tfAge.setText("");
                }
                else{
                    tfName.setText(newCustomer.getName());
                    tfAge.setText(Integer.toString(newCustomer.getAge()));
                }
            });
        }
    }
    

    Details FXML:

    <?xml version="1.0" encoding="UTF-8"?>
    
    <?import javafx.geometry.Insets?>
    <?import javafx.scene.control.Label?>
    <?import javafx.scene.control.TextField?>
    <?import javafx.scene.layout.VBox?>
    
    
    <VBox alignment="CENTER" maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" prefHeight="400.0" prefWidth="600.0" xmlns="http://javafx.com/javafx/8.0.171" xmlns:fx="http://javafx.com/fxml/1" fx:controller="javafxapplication36.DetailsController">
       <children>
          <Label text="Name" />
          <TextField fx:id="tfName" />
          <Label text="Age" />
          <TextField fx:id="tfAge" />
       </children>
       <padding>
          <Insets left="20.0" right="20.0" />
       </padding>
    </VBox>
    

    More info:

    @James D answer on Model-View-Controller(MVC).
    GitHub code.

    0 讨论(0)
  • 2021-01-25 12:53

    The following is a demonstration of two windows (stages) sharing the same model.
    The demonstration is kept as simple as possible: one window displays a list. The second window dynamically displays the items that where selected on the first:

    The shared model holds the information that the two windows need. Basically a list of items, and a list of selected items:

    package two_windows;
    
    import javafx.collections.FXCollections;
    import javafx.collections.ObservableList;
    
    public class Model {
    
        private final ObservableList<String> list;
        private ObservableList<String> selected;
    
        Model(){
            list = FXCollections.observableArrayList();
        }
    
        void addMessage(String msg){
            list.add(msg);
        }
    
        ObservableList<String> getMessages(){
            return list;
        }
    
        ObservableList<String> getSelectedMessages(){
            return selected;
        }
    
        void setSelected(ObservableList<String> selected) {
            this.selected = selected;
        }
    }
    

    The content of the first window is defined by List.fxml and its controller:

    <?xml version="1.0" encoding="UTF-8"?>
    
    <?import javafx.scene.layout.Pane?>
    <?import javafx.scene.control.ListView?>
    
    <Pane xmlns="http://javafx.com/javafx/10.0.1" xmlns:fx="http://javafx.com/fxml/1"
     fx:controller="two_windows.ListController">
       <children>
            <ListView fx:id="list" prefHeight="300.0" prefWidth="150.0" />
        </children>
    </Pane>
    

    The controller accepts a Model, sets the selected items list in the model, listens and responds to model changes:

    package two_windows;
    
    import java.util.List;
    import javafx.collections.ListChangeListener;
    import javafx.fxml.FXML;
    import javafx.scene.control.ListView;
    import javafx.scene.control.SelectionMode;
    
    public class ListController {
    
        @FXML ListView<String> list;
    
        void setModel(Model model) {
    
            list.getSelectionModel().setSelectionMode(SelectionMode.MULTIPLE);//allow multiple selection
    
            //sets the selected items of the list to the model 
            model.setSelected(list.getSelectionModel().getSelectedItems());
    
            //listen to changes in model, and respond
            model.getMessages().addListener(
                                            (ListChangeListener<String>) c -> {
                                                c.next();
                                                addElements(c.getAddedSubList());
                                            }
                                        );
        }
    
        private void addElements(List<? extends String> msgList){
    
            for(String msg : msgList){
                list.getItems().add(msg);
            }
        }
    }
    

    The content of the second window is very similar to the first, and is defined by Selected.fxml:

    <?xml version="1.0" encoding="UTF-8"?>
    
    <?import javafx.scene.layout.Pane?>
    <?import javafx.scene.control.ListView?>  
    
    <Pane xmlns="http://javafx.com/javafx/10.0.1" xmlns:fx="http://javafx.com/fxml/1"
     fx:controller="two_windows.SelectedController">
       <children>
            <ListView fx:id="selected" prefHeight="300.0" prefWidth="150.0" />
        </children>
    </Pane>
    

    And its controller, which like the other controller accepts a Model and responds to changes in it:

    package two_windows;
    
    import java.util.List;
    import javafx.collections.ListChangeListener;
    import javafx.fxml.FXML;
    import javafx.scene.control.ListView;
    
    public class SelectedController {
    
        @FXML ListView<String> selected;
    
        void setModel(Model model) {
    
            //listen to changes in model, and respond
            model.getSelectedMessages().addListener(
                        (ListChangeListener<String>) c -> {
                            c.next();
                            removeElements(c.getRemoved());
                            addElements(c.getAddedSubList());
                        }
                    );
        }
    
        private void removeElements(List<? extends String> msgList){
    
            for(String msg : msgList){
                selected.getItems().remove(msg);
            }
        }
    
        private void addElements(List<? extends String> msgList){
    
            for(String msg : msgList){
                selected.getItems().add(msg);
            }
        }
    }
    

    Putting it all together and testing:

    package two_windows;
    
    import javafx.application.Application;
    import javafx.fxml.FXMLLoader;
    import javafx.scene.Parent;
    import javafx.scene.Scene;
    import javafx.stage.Stage;
    
    public class TwoWindows extends Application {
    
        private Model model;
    
        @Override
        public void start(Stage primaryStage) throws Exception{
    
            model = new Model();
    
            FXMLLoader listLoader = new FXMLLoader(getClass().getResource("List.fxml"));
            Parent list = listLoader.load();
            ListController listController = listLoader.getController();
            listController.setModel(model);
    
            FXMLLoader selectedLoader = new FXMLLoader(getClass().getResource("Selected.fxml"));
            Parent selected = selectedLoader.load();
            SelectedController selectedController = selectedLoader.getController();
            selectedController.setModel(model);
    
            primaryStage.setScene(new Scene(list));
            primaryStage.setX(350); primaryStage.setY(300);
    
            Stage secondaryStage = new Stage();
            secondaryStage.setScene(new Scene(selected));
            secondaryStage.setX(550); secondaryStage.setY(300);
    
            addMessages();
            primaryStage.show();
            secondaryStage.show();
        }
    
        private void addMessages() {
    
            int counter = 0;
            while(counter < 15) {
                model.addMessage("message number "+ counter++);
            }
        }
    
        public static void main(final String[] args) {
            launch(args);
        }
    }
    
    0 讨论(0)
提交回复
热议问题