2

I'm trying to get a JFXTreeTableView column to display both text and an image while continuing to use the tree grouping feature.

I modeled my program after the demo and was able to get StringProperty based columns to work correctly, but now attempting to add ObjectProperty<Label> columns just isn't working. Can anyone give me an example of column using JFXTreeTableView and anything other than a primative or String (i.e. something that uses ObjectProperty<?>)?

Edit: I just made some progress with this by using an HBox for the column I was able to get my image and text to display.

Below is the code I used to get this far...

In my cell object:

public final class TestObj extends RecursiveTreeObject<TestObj> {
        private final ObjectProperty<HBox> source;

        public TestObj(String source) {
                ImageView iv = new ImageView();
                HBox hbox = new HBox();
                hbox.setSpacing(10);
                VBox vbox = new VBox();
                Label l = new Label(source);
                vbox.getChildren().add(l);
                iv.setFitHeight(20);
                iv.setFitWidth(20);
                iv.setImage(new Image("/images/test.png"));
                l.setGraphic(iv);
                hbox.getChildren().addAll(vbox);
                this.source = new SimpleObjectProperty<>(hbox);
        }

        // ... getters & setters
}

In my controller:

public void initialize(URL location, ResourceBundle resources) {
        JFXTreeTableColumn<TestObj, HBox> srcColumn = new JFXTreeTableColumn<>("Source");
        srcColumn.setPrefWidth(150);
        srcColumn.setCellValueFactory((TreeTableColumn.CellDataFeatures<TestObj, HBox> x) ->
        srcColumn.validateValue(x) ? x.getValue().getValue().sourceProperty() : srcColumn.getComputedValue(x));

        //... reset of table setup code
}

Now the problem is the column won't group properly! That is: the image and text could be exactly the same per row, but it won't collapse into a group. Instead, it collapses into many groups with one element each. This makes me think how the table is comparing the cells is the problem... perhaps I need to override a method somewhere?

Edit 2: Now, with the code below, grouping is possible and ungrouped nodes are properly hidden. Additionally, James_D comment was applied to relocate the GUI elements out of the data class. The current problem (outlined in paragraph above) is demonstrated in fig. A and fig. B.

Data object:

public final class TestObj extends RecursiveTreeObject<TestObj> {
        //inapplicable vars omitted from this class
        //so don't ask why there's just an inner class here ;)
        private final ObjectProperty<Register> source;
        private final ObjectProperty<Register> destination;

        public TestObj(String src, String srcPort, String srcCountry, String dst, String dstPort, String dstCountry) {
                this.source = new SimpleObjectProperty<>(new Register(src, srcPort, srcCountry));
                this.destination = new SimpleObjectProperty<>(new Register(dst, dstPort, dstCountry));
        }

        // ... getters & setters

        public class Register {
                private final String address;
                private final String port;
                private final String country;

                public Register(String address, String port, String country) {
                    this.address = address;
                    this.port = port;
                    this.country = country;
                }

                //getters ...
         }
}

Controller:

public void initialize(URL location, ResourceBundle resources) {
        JFXTreeTableColumn<TestObj, TestObj.Register> srcColumn = new JFXTreeTableColumn<>("Source");
        srcColumn.setPrefWidth(150); srcColumn.setCellValueFactory((TreeTableColumn.CellDataFeatures<TestObj, TestObj.Register> x) ->
            srcColumn.validateValue(x) ? x.getValue().getValue().sourceProperty() : srcColumn.getComputedValue(x));
    srcColumn.setCellFactory(new Callback<TreeTableColumn<TestObj, TestObj.Register>, TreeTableCell<TestObj, TestObj.Register>>() {
        @Override
        public TreeTableCell<TestObj, TestObj.Register> call(TreeTableColumn<TestObj, TestObj.Register> param) {
            return new JFXTreeTableCell<TestObj, TestObj.Register>() {
                ImageView iv = new ImageView();
                @Override
                protected void updateItem(TestObj.Register item, boolean empty) {
                    if(item != null) {
                        HBox hbox = new HBox();
                        hbox.setSpacing(10);
                        VBox vbox = new VBox();
                        Label l = new Label(item.getAddress() + ":" + item.getPort());
                        vbox.getChildren().add(l);
                        iv.setFitHeight(20);
                        iv.setFitWidth(20);
                        iv.setImage(new Image("/flags/" + item.getCountry() + ".png"));
                        l.setGraphic(iv);
                        hbox.getChildren().addAll(vbox);
                        setGraphic(hbox);
                    } else {
                        setGraphic(null);
                    }
                }
            };
        }
    });
        //... above duplicated for dstColumn
        //... reset of table setup code
}

Fig. A - Demonstrates the initial view of the table (everything is fine here)

Demonstrates the initial view of the table (everything is fine here)

Fig. B - Demonstrates incorrect grouping (there should be 3 groups each with nodes)

Demonstrates incorrect grouping (there should be 3 groups each with nodes)

  • You seem to have confused the different pieces of this. Your data class (`TestObj`) should not contain any references to any UI components (such as `ImageView` or `HBox`). It should contain *data* only. If you want a custom display in the column, use a `cellFactory` to generate a custom cell. This works essentially the same way as a `TableView`: there is a good tutorial [here](http://code.makery.ch/library/javafx-8-tutorial/part2/) (cell factories are covered [here](http://code.makery.ch/blog/javafx-8-tableview-cell-renderer/)). – James_D Dec 18 '17 at 19:21
  • This problem is no longer about simply rendering images in the table. I have that now, the problem is with the JFXTreeTableView's grouping feature not working correctly with my object columns. There seems to be an issue related to the validateValue method. As a side note, I did correct my code to move the UI components from the data class into an override of the updateItem() method, the grouping issue is still present, however. – TrunkFullOfGoats Dec 18 '17 at 22:49
  • OK, so you want us to help you diagnose code we can't see? – James_D Dec 18 '17 at 23:07
  • Good point! Added updates... – TrunkFullOfGoats Dec 19 '17 at 16:39

1 Answers1

1

After some review of the JFoenix source, I realized there's no magical comparator behind the scenes... in my data Object I only needed to override the equals and hashCode methods to consider the complex object's variables.

In this case:

@Override
public boolean equals(Object o) {
    if(o == this)
        return true;
    if(!(o instanceof Register))
        return false;
    Register r = (Register)o;
    return Objects.equals(address, r.address) 
        && Objects.equals(port, r.port) 
        && Objects.equals(country, r.country);
}

@Override
public int hashCode() {
    return Objects.hash(address, port, country);
}