В моем TableView
одним из значений TableColumn
является ObservableList
. Для упрощения он содержит только StringProperty
значений. В моей фабрике значений ячеек для столбца я объединяю значения вместе, используя класс Bindings
.
Результат, которого я хочу, должен быть StringExpression
, который обновляется всякий раз, когда изменяется ObservableList
.
Проблема, с которой я сталкиваюсь, заключается в том, что при изменении любого из значений списка сгенерированное StringExpression
обновляется правильно, хотя, когда я добавляю или удаляю значения в списке, он не будет обновляться.
Моей первой мыслью было добавить к строке ListChangeListener
, но это кажется нецелесообразным, поскольку в таблице могут быть десятки или сотни строк.
import java.util.ArrayList;
import java.util.List;
import javafx.application.Application;
import javafx.beans.binding.Bindings;
import javafx.beans.property.SimpleStringProperty;
import javafx.beans.property.StringProperty;
import javafx.beans.value.ObservableValue;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.TableColumn;
import javafx.scene.control.TableColumn.CellDataFeatures;
import javafx.scene.control.TableView;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.HBox;
import javafx.stage.Stage;
import javafx.util.Callback;
public class TestFx extends Application {
@Override
public void start(Stage primaryStage) {
//Note, I never really see this "row" value directly, they're loaded
//in from a database, and there can be dozens or hundreds of
//rows for the table, so I don't if adding a listener to each one
//is the best idea.
final ObservableList<StringProperty> row = FXCollections.observableArrayList();
row.add(new SimpleStringProperty("test"));
row.add(new SimpleStringProperty("one"));
row.add(new SimpleStringProperty("two"));
TableView<ObservableList<StringProperty>> table = new TableView();
table.setColumnResizePolicy(TableView.CONSTRAINED_RESIZE_POLICY);
TableColumn<ObservableList<StringProperty>, String> concatColumns = new TableColumn<>("Concatentated Values");
concatColumns.setCellValueFactory(new Callback<CellDataFeatures<ObservableList<StringProperty>, String>, ObservableValue<String>>() {
@Override
public ObservableValue<String> call(CellDataFeatures<ObservableList<StringProperty>, String> p) {
ObservableList<StringProperty> row = p.getValue();
List toConcat = new ArrayList();
for (int i = 0; i < row.size(); i++) {
toConcat.add(row.get(i));
if (i+1 < row.size()) {
toConcat.add(", ");
}
}
return Bindings.concat(toConcat.toArray());
}
});
table.getColumns().add(concatColumns);
table.getItems().add(row);
BorderPane root = new BorderPane();
HBox buttons = new HBox();
Button add = new Button();
add.setText("Add Item");
add.setOnAction(new EventHandler<ActionEvent>() {
@Override
public void handle(ActionEvent event) {
row.add(new SimpleStringProperty("added"));
}
});
Button remove = new Button();
remove.setText("Remove Item");
remove.setOnAction(new EventHandler<ActionEvent>() {
@Override
public void handle(ActionEvent event) {
if (row.size() > 0) {
row.remove(0);
}
}
});
Button change = new Button();
change.setText("Change Item");
change.setOnAction(new EventHandler<ActionEvent>() {
@Override
public void handle(ActionEvent event) {
if (row.size() > 0) {
row.get(0).set("Changed");
}
}
});
buttons.getChildren().addAll(add, remove, change);
root.setCenter(table);
root.setBottom(buttons);
Scene scene = new Scene(root, 300, 250);
primaryStage.setScene(scene);
primaryStage.show();
}
public static void main(String[] args) {
launch(args);
}
}
Я знаю, "почему" это не работает. StringExpression
связано со значениями StringProperty
списка, а не с самим списком. Поэтому всякий раз, когда я добавляю/удаляю значения, они не обновляются.
Однако, когда я удаляю значения (удаляя первый индекс), это заставляет StringExpression
переоценивать себя (я полагаю?), и в этом случае появляются новые добавленные/удаленные значения. (Нажмите Удалить, а затем Изменить)
Простое добавление значений не поможет. (Нажмите «Добавить», затем «Изменить»)
Итак, как в обратном вызове фабрики значений ячеек я работаю со своим кодом, чтобы прослушивать ObservableList
и возвращать ObservableValue
, который может объединять список и отображать, когда происходят изменения, включая добавление/удаление?