JavaFx: How to compare values of dynamically created TextFields inside GridPane?

后端 未结 2 1420
星月不相逢
星月不相逢 2021-01-15 03:07

I\'m developing an application using JavaFx in which I\'m creating dynamic TextFields inside a GridPane and there is a Button which is initially disabled like this:

相关标签:
2条回答
  • 2021-01-15 03:45

    Actually it's not that easy to do what you want, because the code you have needs to be refactored (the code is not meant to do such advanced requirements but it's fine for the basic requirements you have). However, you can do something like this:

    First, define a global variable to be updated with the last row index of the invalid TextField (From here you shall conclude that this will change the border color for ONE invalid TextField at a time):

    public static int textFieldIndex = -1;
    

    Now with the help of the method you already have getComponent (int row, int column, GridPane table), create another static method to check if ALL TextFields have Valid Values at one time:

    /**
    * This method to check at run time with every change in any TextField 
    * if the corresponding TextField has a valid value(i.e contains number and
    * the first TextField value is less than the second)
    * @param table
    * @param numRows
    */
    private static boolean hasValidValue(GridPane table, int numRows){
       // cycle through every row in the table
       // and compare every two TextFields
       for(int i=0; i<numRows; i++){
          try{ // try because user may enters a non-number input (to avoid crash)
             // the first TextField is always at column index 0 , the second at column index 3
             if(Integer.parseInt(((TextField)(getComponent (i, 0, table))).getText())>
                Integer.parseInt(((TextField)(getComponent (i, 3, table))).getText())){
                // before returning false
                textFieldIndex = i; // update at which row the TextField is less
                return false;
              }
           }catch(NumberFormatException e){ // if it contains invalid input(non-digit)
                return false;
           }
       }
       return true; 
    }
    

    Now you need to use the above method in the validateTable() method and do some adjustments:

    // pass the comboBox.getValue() to the third parameter
    private void validateTable(GridPane table, Button button, int numRows) {
    
       for(Node textField : table.getChildren()){ 
          if(textField instanceof TextField){
             ((TextField)textField).textProperty().addListener((obs, old, newV)->{
               // first of all remove the red border from the invalid TextField (if any)
              // we know that via textFieldIndex which should be -1 if there is no lesser
              // actually it's a pain 
              if(textFieldIndex!=-1){
                ((TextField) getComponent(textFieldIndex, 3, table)).setStyle("");
              }
              if(isAllFilled(table)){ // if all filled ( you already have this method)
                 if(hasValidValue(table,numRows)){ // check for validity
                    button.setDisable(false); // then make the button active again
                 }
                 else{// if it's not a valid value
                      // re-style the TextField which has lesser value
                     ((TextField) getComponent(textFieldIndex, 3, table)).
                                            setStyle("-fx-border-color: red;");
                      button.setDisable(true); 
                 }
              }
              else{
                   button.setDisable(true);
              }
           });
         }  
       }
    }
    

    Now in your tabPane ChangeListener add the third para to the method (because you already have it you need just to add the value of ComboBox:

    tabPane.getSelectionModel().selectedItemProperty().addListener(new ChangeListener<Tab>(){
       ....
       ....
       ....
       // I think you have here anchorPane not containerB in the original code
       validateTable((GridPane) containerB.getChildren().get(0), test, comboBox.getValue());
    }
    

    Test

    0 讨论(0)
  • 2021-01-15 04:10

    You can create the bindings that do the validation when you create the text fields. This will avoid the need to navigate through the grid pane's child nodes, which doesn't seem very robust.

    Declare an array of boolean bindings (there will be one for each row):

    private BooleanBinding[] rowValidationBindings ;
    

    Then you can do

    public static GridPane table(int rows){
        GridPane table = new GridPane();
    
        rowValidationBindings = new BooleanBinding[rows];
    
        for(int i=0; i<rows; i++){
            TextField textField1 = new JFXTextField();
            textField1.setAlignment(Pos.CENTER);
            TextField textField2 = new JFXTextField();
            textField2.setAlignment(Pos.CENTER);
            TextField textField3 = new JFXTextField();
            textField3.setAlignment(Pos.CENTER);
    
            rowValidationBindings[i] = Bindings.createBooleanBinding(
                () -> {
                    if (textField1.getText().matches("\\d+") &&
                        textField3.getText().matches("\\d+")) {
                        int value1 = Integer.parseInt(textField1.getText());
                        int value3 = Integer.parseInt(textFIeld3.getText());
                        return value3 > value1 ;
                    } else {
                        return false ;
                    }
                }, textField1.textProperty(), textField2.textProperty()
            );
    
            //add them to the GridPane
            table.add(textField1, 0, i+1);
            table.add(textField2, 1, i+1);
            table.add(textField3, 2, i+1);
        }
    
        button.disableProperty().bind(Bindings.createBooleanBinding(
            () -> ! Stream.of(rowValidationBindings).allMatch(BooleanBinding::get),
            rowValidationBindings
        ));
    
        return table;
    }
    

    You can also add the styling to the text field directly in the for loop:

    textField3.styleProperty().bind(Bindings
        .when(rowValidationBindings[i])
        .then("")
        .otherwise("-fx-border-color: red")); // or whatever you are using for style
    

    and for tooltips:

    Tooltip tooltip = new Tooltip();
    tooltip.textProperty().bind(Bindings.concat("Value must be greater than ",textField1.textProperty()));
    textField3.tooltipProperty().bind(Bindings
        .when(rowValidationBindings[i])
        .then((Tooltip)null)
        .otherwise(tooltip));
    
    0 讨论(0)
提交回复
热议问题