【问题标题】:JavaFx: How to implement dynamic Gridpane?JavaFx:如何实现动态 Gridpane?
【发布时间】:2017-10-21 11:00:24
【问题描述】:

我正在用 JavaFx 开发一个应用程序,其中有两个选项卡。

在第一个选项卡中我有 ComboBox:

在第二个选项卡中,我有这样的 Gridpane:

我想要的是当用户从 Tab A 的组合框中选择让说 3 时,例如:

它应该在选项卡 B 的网格窗格中添加 3 行,每列包含文本字段、复选框和日期选择器。 A 列有文本字段,B 列有复选框,C 列有 DatePicker,如下所示:

请帮助我如何实现这一点以及实现后如何访问每个文本字段、复选框和日期选择器的数据。

更新:尝试用 FXML 做@Yahya 解决方案

Main.java

public class Main extends Application {
    @Override
    public void start(Stage primaryStage) {
        try {
            TabPane root = (TabPane)FXMLLoader.load(getClass().getResource("Sample.fxml"));
            Scene scene = new Scene(root,400,400);
            scene.getStylesheets().add(getClass().getResource("application.css").toExternalForm());
            primaryStage.setScene(scene);
            primaryStage.show();
        } catch(Exception e) {
            e.printStackTrace();
        }
    }

    public static void main(String[] args) {
        launch(args);
    }
}

SampleController.java

public class SampleController {

    @FXML
    private TabPane root ;
    @FXML
    private Tab tabA ;
    @FXML
    private Tab tabB ;
    @FXML
    private ComboBox<Integer> comboBox ;
    @FXML
    private static GridPane gridPane ;
    @FXML
    private AnchorPane anchB ;


public void initialize() {

    // Create a comboBox, set its attributes and add it to container
    comboBox.getItems().addAll(1,2,3,4,5,6,7,8,9,10);
    comboBox.setValue(1);
    comboBox.setEditable(false);
   // anchA.getChildren().add(comboBox);

    // add listener to tabPane
    root.getSelectionModel().selectedItemProperty().addListener(new ChangeListener<Tab>(){
         @Override
         public void changed(ObservableValue<? extends Tab> observable, Tab oldTab, Tab newTab){
              if(newTab == tabB) { // when tabB is selected
                  System.out.println(anchB.getChildren().size());
                  if(anchB.getChildren().size()<=0){ // if already contains a table

                      anchB.getChildren().add(table(comboBox.getValue())); 

                      table(comboBox.getValue());
                      System.out.println("hello");
                  }
                  else {
                  anchB.getChildren().remove(gridPane); // remove it
                  System.out.println("no");
                  }
            }
       }   
   });
}

//This static method shall create the table dynamically when it's called
// you can add, change and remove the attributes of the table components
// the colors and all other decoration(e.g. position) are for example
public static GridPane table(int rows){

    for(int i=0; i<rows; i++){
        TextField textField = new TextField();
        textField.setAlignment(Pos.CENTER);
        CheckBox checkBox = new CheckBox("Check Box");
        checkBox.setTextFill(Color.WHITE);
        checkBox.setAlignment(Pos.CENTER);
        DatePicker datePicker = new DatePicker();

        //add them to the GridPane
        gridPane.add(textField, 0, i+1); //  (child, columnIndex, rowIndex)
        gridPane.add(checkBox , 1, i+1);
        gridPane.add(datePicker,2, i+1);

        // margins are up to your preference
        GridPane.setMargin(textField, new Insets(5));
        GridPane.setMargin(checkBox, new Insets(5));
        GridPane.setMargin(datePicker, new Insets(5));
     }

    return gridPane;

}
}

Sample.fxml

<?xml version="1.0" encoding="UTF-8"?>

<?import javafx.scene.control.ComboBox?>
<?import javafx.scene.control.Tab?>
<?import javafx.scene.control.TabPane?>
<?import javafx.scene.layout.AnchorPane?>
<?import javafx.scene.layout.ColumnConstraints?>
<?import javafx.scene.layout.GridPane?>
<?import javafx.scene.layout.RowConstraints?>
<?import javafx.scene.text.Text?>

<TabPane fx:id="root" maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" prefHeight="400.0" prefWidth="600.0" tabClosingPolicy="UNAVAILABLE" xmlns="http://javafx.com/javafx/8.0.111" xmlns:fx="http://javafx.com/fxml/1" fx:controller="application.SampleController">
   <tabs>
      <Tab fx:id="tabA" text="Tab A">
         <content>
            <AnchorPane fx:id="anchA" minHeight="0.0" minWidth="0.0" prefHeight="180.0" prefWidth="200.0">
               <children>
                  <ComboBox fx:id="comboBox" layoutX="225.0" layoutY="82.0" prefWidth="150.0" />
               </children>
            </AnchorPane>
         </content>
      </Tab>
      <Tab fx:id="tabB" text="Tab B">
         <content>
            <AnchorPane fx:id="anchB" minHeight="0.0" minWidth="0.0" prefHeight="180.0" prefWidth="200.0">
               <children>
                  <GridPane fx:id="gridPane" gridLinesVisible="true" layoutX="150.0" layoutY="62.0">
                     <columnConstraints>
                        <ColumnConstraints hgrow="SOMETIMES" minWidth="10.0" prefWidth="100.0" />
                        <ColumnConstraints hgrow="SOMETIMES" minWidth="10.0" prefWidth="100.0" />
                        <ColumnConstraints hgrow="SOMETIMES" minWidth="10.0" prefWidth="100.0" />
                     </columnConstraints>
                     <rowConstraints>
                        <RowConstraints minHeight="10.0" prefHeight="30.0" vgrow="SOMETIMES" />
                     </rowConstraints>
                     <children>
                        <Text strokeType="OUTSIDE" strokeWidth="0.0" text="A" textAlignment="CENTER" wrappingWidth="91.67529296875" />
                        <Text strokeType="OUTSIDE" strokeWidth="0.0" text="B" textAlignment="CENTER" wrappingWidth="93.5986328125" GridPane.columnIndex="1" />
                        <Text strokeType="OUTSIDE" strokeWidth="0.0" text="C" textAlignment="CENTER" wrappingWidth="95.287109375" GridPane.columnIndex="2" />
                     </children>
                  </GridPane>
               </children>
            </AnchorPane>
         </content>
      </Tab>
   </tabs>
</TabPane>

【问题讨论】:

    标签: java user-interface javafx javafx-2 javafx-8


    【解决方案1】:

    你可以这样做:

    import javafx.application.Application;
    import javafx.beans.value.ChangeListener;
    import javafx.beans.value.ObservableValue;
    import javafx.geometry.Insets;
    import javafx.geometry.Pos;
    import javafx.scene.Scene;
    import javafx.scene.control.CheckBox;
    import javafx.scene.control.ComboBox;
    import javafx.scene.control.DatePicker;
    import javafx.scene.control.Tab;
    import javafx.scene.control.TabPane;
    import javafx.scene.control.TextField;
    import javafx.scene.layout.Background;
    import javafx.scene.layout.BackgroundFill;
    import javafx.scene.layout.GridPane;
    import javafx.scene.layout.Pane;
    import javafx.scene.layout.StackPane;
    import javafx.scene.paint.Color;
    import javafx.stage.Stage;
    
    public class DynamicTable extends Application{
    
        @Override
        public void start(Stage ps) throws Exception {
    
            // Create a Tab A, create a container and add it 
            Tab tabA = new Tab("Tab A");
            StackPane containerA = new StackPane();
    
            // note that the colors are for example
            containerA.setBackground(new Background(
                    new BackgroundFill(Color.MAROON,null,null)));
            // Create a comboBox, set its attributes and add it to container
            ComboBox<Integer> comboBox = new ComboBox<Integer>();
            comboBox.getItems().addAll(1,2,3,4,5,6,7,8,9,10);
            comboBox.setValue(1);
            comboBox.setEditable(false);
            containerA.getChildren().add(comboBox);
    
            //add the container to the tabA
            tabA.setContent(containerA);
    
            // Create Tab B, create a container and add it 
            Tab tabB = new Tab("Tab B");
            StackPane containerB = new StackPane();
    
            containerB.setBackground(new Background(
                            new BackgroundFill(Color.DARKMAGENTA,null,null)));
            tabB.setContent(containerB);
            // create TabPane and add the Tabs to it
            // all other values need manipulation (i.e. up to your preference)
            TabPane tabPane = new TabPane();
             tabPane.getTabs().addAll(tabA, tabB);
            //set size and other attributes (if any), for example 
            tabPane.setMinWidth(500);
            tabPane.setMinHeight(500);
    
             // add listener to tabPane
             tabPane.getSelectionModel().selectedItemProperty().addListener(new ChangeListener<Tab>(){
                  @Override
                  public void changed(ObservableValue<? extends Tab> observable, Tab oldTab, Tab newTab){
                       if(newTab == tabB) { // when tabB is selected
                           if(containerB.getChildren().size()>0){ // if already contains a table
                               containerB.getChildren().remove(0); // remove it
                           }
                           containerB.getChildren().add(table(comboBox.getValue())); // create the table and add it   
                     }
                }   
            });
    
            // simple root to test 
            Pane root = new Pane();
            root.getChildren().add(tabPane);
    
            Scene scene = new Scene(root, 500,500);
            ps.setScene(scene);
            ps.setTitle("Dynamic Table In Tab");
            ps.show();
        }
    
    
        // This static method shall create the table dynamically when it's called
        // you can add, change and remove the attributes of the table components
        // the colors and all other decoration(e.g. position) are for example
        public static GridPane table(int rows){
            GridPane table = new GridPane();
    
            for(int i=0; i<rows; i++){
                TextField textField = new TextField();
                textField.setAlignment(Pos.CENTER);
                CheckBox checkBox = new CheckBox("Check Box");
                checkBox.setTextFill(Color.WHITE);
                checkBox.setAlignment(Pos.CENTER);
                DatePicker datePicker = new DatePicker();
    
                //add them to the GridPane
                table.add(textField, 0, i); //  (child, columnIndex, rowIndex)
                table.add(checkBox , 1, i);
                table.add(datePicker,2, i);
    
                // margins are up to your preference
                GridPane.setMargin(textField, new Insets(5));
                GridPane.setMargin(checkBox, new Insets(5));
                GridPane.setMargin(datePicker, new Insets(5));
             }
            table.setAlignment(Pos.CENTER);
    
            return table;
    
        }
    
        public static void main(String[] args) {
            launch();
    
        }
    }
    

    测试

    更新: 如果您想从TabB 中的表中获取值,您可以执行以下操作:

    // first create a method like this
    // this static method to return a component from Table at specific row and column
    public static Node getComponent (int row, int column, GridPane table) {
        for (Node component : table.getChildren()) { // loop through every node in the table
            if(GridPane.getRowIndex(component) == row && 
                            GridPane.getColumnIndex(component) == column) {
                return component;
            }
        }
    
        return null;
    }
    
    // Then use the method like this
    // The textfield always at Column 0, check box column 1 and Date picker 2
    // and the GridPane is in a StackPane(container) at index 0
    // Note that you may NEED to add a button and wrap the following code with its Action Listener (i.e. button.setOnAction())
    if(containerB.getChildren().size()>0){ // that means it contains a table    
        GridPane table = (GridPane) containerB.getChildren().get(0);
        for(int i=0 ; i<comboBox.getValue(); i++){
            String Text = ((TextField)getComponent (i, 0, table)).getText();
            boolean selected = ((CheckBox)getComponent (i, 1, table)).isSelected();
            LocalDate date = ((DatePicker)getComponent (i, 2, table)).getValue();
    
            System.out.println(Text + " " + selected + " " + date);
            System.out.println("Next Row");
        }
    
    }
    

    更新:

    如果您希望您的程序仅在用户更改 ComboBox 中的值时删除/创建表 (GridPane),那么您需要将 ChangeListener 添加到您的 ComboBox 以捕获更改因此,您需要一个 global boolean,当 ComboBox 值更改时,它会从 true 更改为 false,反之亦然,并且此 boolean 将在if-statementthat 检查表是否已存在于containerB 中(在您更新的问题anchB 中),例如:

    boolean valueChanged = false; // its scope should be global and to be added to the ComboBox ChangeListener
    if(containerB.getChildren().size()>0 && valueChanged){ // if already contains a table
        containerB.getChildren().remove(0); // remove it
    }
    

    【讨论】:

    • 感谢您的回答@Yahya,如果我使用 FXML 构建 UI,您能告诉我如何实现吗?
    • @Junaid 您应该在您的问题中提到,但是,我为您的问题创建了一个完整的程序,如果有任何帮助,请接受它。关于FXML中的组件,给每个组件一个id,然后使用查找方法docs.oracle.com/javase/8/javafx/api/javafx/scene/…
    • 我可以用 FXML 做到这一点,但问题是我无法删除 Gridpane,它在线程“JavaFX 应用程序线程”java.lang.NullPointerException 中抛出“异常”。
    • root.getSelectionModel().selectedItemProperty().addListener(new ChangeListener&lt;Tab&gt;(){ @Override public void changed(ObservableValue&lt;? extends Tab&gt; observable, Tab oldTab, Tab newTab){ if(newTab == tabB) { // when tabB is selected System.out.println(anchB.getChildren().size()); if(anchB.getChildren().size()&gt;0){ // if already contains a table anchB.getChildren().remove(root); } anchB.getChildren().add(table(comboBox.getValue())); table(comboBox.getValue()); } } });
    • @Junaid,你可以这样做:boolean valueChanged = false;comboBox().valueProperty().addListener((obs, oldV, newV)-&gt;{ if(newV != null){valueChanged = true;} });if(containerB.getChildren().size()&gt;0 &amp;&amp; valueChanged){ containerB.getChildren().remove(0); valueChanged = false; // change it back to false }//////////////////// //////////// 或者更简单:comboBox().valueProperty().addListener((obs, oldV, newV)-&gt;{ if(newV!=null &amp;&amp; containerB.getChildren().size()&gt;0){ containerB.getChildren().remove(0); } });
    猜你喜欢
    • 2014-04-13
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-10-04
    • 2021-03-17
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多