Steinliiippp
Steinliiippp

Reputation: 371

Two different row factories for TableView

In my program have an TableView and I want to register two different row factories to format my rows based on the content.

Here are my factories:

private void highlightReportRowsIfImportant() {
        tv_berichte.setRowFactory(new Callback<TableView<DatabaseReport>, TableRow<DatabaseReport>>() {
            @Override
            public TableRow<DatabaseReport> call(TableView<DatabaseReport> tableView) {
                final TableRow<DatabaseReport> row = new TableRow<DatabaseReport>() {
                    @Override
                    protected void updateItem(DatabaseReport report, boolean empty) {
                        super.updateItem(report, empty);
                        if (report != null) {
                            if (report.getReport_art().contains("!!!")) {
                                setStyle("-fx-background-color: #FF0000;");
                            } else {
                                setStyle("");
                            }
                        } else {
                            setStyle("");
                        }
                    }
                };
                reports.addListener(new ListChangeListener<DatabaseReport>() {
                    @Override
                    public void onChanged(ListChangeListener.Change<? extends DatabaseReport> change) {
                        if (row.getItem() != null) {
                            if (row.getItem().getReport_art().contains("!!!")) {
                                row.setStyle("-fx-background-color: #FF0000;");
                            } else {
                                row.setStyle("");
                            }
                        } else {
                            row.setStyle("");
                        }
                    }
                });
                return row;
            }
        });
    }

And here is the other one:

private void registerDragAndDropReportListener(TableView view) {
    view.setRowFactory(tv -> {
        TableRow<DatabaseReport> row = new TableRow<>();

        row.setOnDragDetected(event -> {
            if (!row.isEmpty()) {
                Dragboard db = row.startDragAndDrop(TransferMode.COPY);
                db.setDragView(row.snapshot(null, null));
                ClipboardContent cc = new ClipboardContent();
                cc.putString(row.getItem().getReport_content());
                db.setContent(cc);
                event.consume();
            }
        });

        row.setOnDragOver(event -> {
            Dragboard db = event.getDragboard();
            if (db.hasContent(DataFormat.PLAIN_TEXT)) {
                event.acceptTransferModes(TransferMode.COPY_OR_MOVE);
                event.consume();
            }
        });

        row.setOnDragDropped(event -> {
            Dragboard db = event.getDragboard();
            if (db.hasContent(DataFormat.PLAIN_TEXT)) {
                reportFlowController.addNewReport(username, getSqlTimeStamp().toString(), this.txt_adressnummer.getText(), reports, db.getString());
                event.setDropCompleted(true);
                event.consume();
            }
        });

        return row;
    });
}

Now if I register the factories like this:

registerDragAndDropReportListener(tv_berichte);
highlightReportRowsIfImportant();

only the last one is used.

Could you please explain me, how I can register both of them?

Upvotes: 0

Views: 437

Answers (2)

Steinliiippp
Steinliiippp

Reputation: 371

Here is my solution, using both listeners in one row factory with the same row object. After you postet your awnser James_D, it was clear for me. I cant register 2 of them, which should be handelt ... first think, then write code ...

private void highlightReportRowsIfImportant(TextField txt_adressnummer) {
            tv_berichte.setRowFactory(new Callback<TableView<DatabaseReport>, TableRow<DatabaseReport>>() {
                @Override
                public TableRow<DatabaseReport> call(TableView<DatabaseReport> tableView) {
                    final TableRow<DatabaseReport> row = new TableRow<DatabaseReport>() {
                        @Override
                        protected void updateItem(DatabaseReport report, boolean empty) {
                            super.updateItem(report, empty);
                            if (report != null) {
                                if (report.getReport_art().contains("!!!")) {
                                    setStyle("-fx-background-color: #FF0000;");
                                } else {
                                    setStyle("");
                                }
                            } else {
                                setStyle("");
                            }
                        }
                    };
                    reports.addListener(new ListChangeListener<DatabaseReport>() {
                        @Override
                        public void onChanged(ListChangeListener.Change<? extends DatabaseReport> change) {
                            if (row.getItem() != null) {
                                if (row.getItem().getReport_art().contains("!!!")) {
                                    row.setStyle("-fx-background-color: #FF0000;");
                                } else {
                                    row.setStyle("");
                                }
                            } else {
                                row.setStyle("");
                            }
                        }
                    });

                    row.setOnDragDetected(event -> {
                        if (!row.isEmpty()) {
                            Dragboard db = row.startDragAndDrop(TransferMode.COPY);
                            db.setDragView(row.snapshot(null, null));
                            ClipboardContent cc = new ClipboardContent();
                            cc.putString(row.getItem().getReport_content());
                            db.setContent(cc);
                            event.consume();
                        }
                    });

                    row.setOnDragOver(event -> {
                        Dragboard db = event.getDragboard();
                        if (db.hasContent(DataFormat.PLAIN_TEXT)) {
                            event.acceptTransferModes(TransferMode.COPY_OR_MOVE);
                            event.consume();
                        }
                    });

                    row.setOnDragDropped(event -> {
                        Dragboard db = event.getDragboard();
                        if (db.hasContent(DataFormat.PLAIN_TEXT)) {
                            reportFlowController.addNewReport(username, getSqlTimeStamp().toString(), txt_adressnummer.getText(), reports, db.getString());
                            event.setDropCompleted(true);
                            event.consume();
                        }
                    });
                    return row;
                }
            });
        }

Upvotes: 0

James_D
James_D

Reputation: 209674

You can't register multiple row factories, and it makes no sense to try to do so. The TableView uses its row factory to create a TableRow object whenever it needs one (typically when it is first laid out on the screen, and perhaps later if it grows vertically and needs to display more rows); remember the row factory is just a function that provides TableRow objects. If two or more row factories were registered, the TableView would have no mechanism for choosing which one to use.

What you can do is define a row factory which uses an existing row factory to generate its row. This effectively replaces the existing row factory with one that adds additional functionality to it:

private void registerDragAndDropReportListener(TableView<DatabaseReport> view) {

    Callback<TableView<DatabaseReport>, TableRow<DatabaseReport>> existingRowFactory 
            = view.getRowFactory();

    view.setRowFactory(tv -> {



        TableRow<DatabaseReport> row ;
        if (existingRowFactory == null) {
            row = new TableRow<>();
        } else {
            row = existingRowFactory.call(view);
        }

        row.setOnDragDetected(event -> {
            if (!row.isEmpty()) {
                Dragboard db = row.startDragAndDrop(TransferMode.COPY);
                db.setDragView(row.snapshot(null, null));
                ClipboardContent cc = new ClipboardContent();
                cc.putString(row.getItem().getReport_content());
                db.setContent(cc);
                event.consume();
            }
        });

        row.setOnDragOver(event -> {
            Dragboard db = event.getDragboard();
            if (db.hasContent(DataFormat.PLAIN_TEXT)) {
                event.acceptTransferModes(TransferMode.COPY_OR_MOVE);
                event.consume();
            }
        });

        row.setOnDragDropped(event -> {
            Dragboard db = event.getDragboard();
            if (db.hasContent(DataFormat.PLAIN_TEXT)) {
                reportFlowController.addNewReport(username, getSqlTimeStamp().toString(), this.txt_adressnummer.getText(), reports, db.getString());
                event.setDropCompleted(true);
                event.consume();
            }
        });

        return row;
    });
}

With this, the registerDragAndDropReportListener method replaces the existing row factory with one that adds additional functionality to the existing row factory, which I think is what you want. So in this case you would first call your original highlightRowsIfImportant method, and then call the new registerDragAndDropReportListener method shown.

You could make similar changes to the other method, so that you could call the two methods in any order you want. Note that in this case, it would be important to ensure you only call each method once.

Upvotes: 1

Related Questions