JavaFX - TableColumn graphic higher than other rows

83 views Asked by At

I am making a table with JavaFX. Every row has text. One row has a graphic because the text of that cell has multiple colors.

The code only applies when a certain condition is true (that part works):

departTimeCol.setCellFactory(column -> new TableCell<Ride, String>() {
            @Override
            protected void updateItem(String item, boolean empty) {
                super.updateItem(item, empty);

                setText(item);

                if(item != null && ! empty){
                    if(item.matches("^([0-9]|0[0-9]|1[0-9]|2[0-3]):[0-5][0-9]\\s\\+[\\d]")) {
                        Text timeText = new Text(item.split(" ")[0].trim() + " ");
                        Text delayText = new Text(item.split(" ")[1].trim());

                        delayText.setFill(Color.RED);

                        TextFlow flow = new TextFlow(timeText, delayText);

                        setText(null);
                        setGraphic(flow);

                    }
                }
            }
        });

The result is:

Result

The row with the red +2 is the graphic. All the other rows contain text. How can I give the row - containing a graphic - the same height?

1

There are 1 answers

1
fabian On BEST ANSWER

Simply set the prefered height to 0 to make the height just what is needed to store the text.

Pattern pattern = Pattern.compile("((?:[0-9]|[01][0-9]|2[0-3]):[0-5][0-9]\\s)(\\+\\d)");

departTimeCol.setCellFactory(column -> new TableCell<Ride, String>() {

    private final Text timeText = new Text();
    private final Text delayText = new Text();
    private final TextFlow flow = new TextFlow(timeText, delayText);

    {
        delayText.setFill(Color.RED);
        flow.setPrefHeight(0);
        flow.heightProperty().addListener((observable, oldValue, newValue) -> {
            this.setMinHeight(newValue.doubleValue() + 4);
        });
        flow.setMinHeight(Region.USE_COMPUTED_SIZE);
        setContentDisplay(ContentDisplay.GRAPHIC_ONLY);
        setGraphic(flow);
    }

    @Override
    protected void updateItem(String item, boolean empty) {
        super.updateItem(item, empty);

        if (empty || item == null) {
            timeText.setText("");
            delayText.setText("");
            delayText.setVisible(false);
        } else {
            Matcher m = pattern.matcher(item);
            if (m.matches()) {
                timeText.setText(m.group(1));
                delayText.setText(m.group(2));
                delayText.setVisible(true);
            } else {
                timeText.setText(item);
                delayText.setText("");
                delayText.setVisible(false);
            }
        }

    }
});

Note that there are a few more things in the code that should be fixed:

  • You never set the graphic back to null, even if the String no longer matches the regex or if the cell becomes empty. This means you can get the TableCell into a state where the text property is not empty and the graphic contains a TextFlow. Note: Always make sure the state of the look of a cell is correct no matter how often the updateItem method is called and independent of the arguments passed.
  • You produce a inconsistent look by using the graphic + a TextFlow for one case and the text property for another. (Just take a look at the leftmost part of the text in your screenshot! Those are not properly aligned).
  • The whole purpose of using Cells is reusing the node to prevent unnecessary creation of nodes. You kind of ruin this attempt by recreating the TextFlow, ect. in the updateItem method instead of reusing those nodes.
  • The regex needs not start with ^ since matches already makes sure the whole input is matched. Furthermore the delimiter used for split does not have an exact equivalent in the regex. There are other space chars than , such as tab. Just check what the following code does...

    System.out.println("a\tb".matches(".\\s."));
    System.out.println("a\tb".split(" ")[1]);
    

    You can also parse the input and match it in the same step by using Pattern+Matcher and capturing groups. This way you also do not have the issue mentioned above.