解决此问题的一种方法是将侦听器添加到TreeCell构造函数中的“现金流”列表中。
public MyTreeCell() {
ListChangeListener<? super Integer> listener = p -> updateItem(getItem(), false);
itemProperty().addListener((obs, oldItem, newItem) -> {
if (oldItem != null) {
oldItem.getCashFlows().removeListener(listener);
}
if (newItem != null) {
newItem.getCashFlows().addListener(listener);
}
});
}
在上面的代码中,您注册了一个监听器来监听与每个单元格关联的项目的现金流。因此,每当列表(现金流)上发生更新时,将调用updateItem来重新评估显示。
[更新]:
基于这些评论和建议,我尝试将提取器实现包含到TreeItem中,以触发值更改事件。而且效果很好:)。这就是@kleopatra和@jewelsea提到的实现。这样,您就可以列出要监视和更新单元格的所有可观察属性。
Callback<Task, Observable[]> extractor = task -> new Observable[]{task.getCashFlows()};
class MyTreeItem<T> extends TreeItem<T> {
public MyTreeItem(Callback<T, Observable[]> extractor) {
if (extractor == null) {
throw new NullPointerException("Extractor cannot be null");
}
final InvalidationListener listener = e -> Event.fireEvent(this, new TreeModificationEvent<>(TreeItem.<T>valueChangedEvent(), this, getValue()));
valueProperty().addListener((obs, oldValue, newValue) -> {
if (oldValue != null) {
Stream.of(extractor.call(oldValue)).forEach(prop -> prop.removeListener(listener));
}
if (newValue != null) {
Stream.of(extractor.call(newValue)).forEach(prop -> prop.addListener(listener));
}
});
}
}
完整的工作演示如下:
使用提取器方法
import javafx.application.Application;
import javafx.beans.InvalidationListener;
import javafx.beans.Observable;
import javafx.beans.property.BooleanProperty;
import javafx.beans.property.SimpleBooleanProperty;
import javafx.beans.property.SimpleStringProperty;
import javafx.beans.property.StringProperty;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.event.Event;
import javafx.geometry.Insets;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.TreeCell;
import javafx.scene.control.TreeItem;
import javafx.scene.control.TreeView;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;
import javafx.util.Callback;
import java.util.Random;
import java.util.stream.Stream;
public class TreeViewDemo extends Application {
public static void main(String[] args) {
launch(args);
}
private Task demoTask;
@Override
public void start(Stage primaryStage) {
// BUILD DATA
Random rnd = new Random();
ObservableList<Task> tasks = FXCollections.observableArrayList();
for (int i = 1; i < 10; i++) {
Task sub1 = new Task("Sub Task A", rnd.nextBoolean());
Task sub2 = new Task("Sub Task B", rnd.nextBoolean());
Task tsk = new Task("Task " + i, rnd.nextBoolean());
if (demoTask == null) {
tsk.setName("Demo Task");
demoTask = tsk;
}
tsk.getTasks().addAll(sub1, sub2);
tasks.addAll(tsk);
}
// BUILD TREE ITEMS
TreeItem<Task> rootItem = new TreeItem<>();
rootItem.setExpanded(true);
final Callback<Task, Observable[]> extractor = task -> new Observable[]{task.getCashFlows()};
for (Task task : tasks) {
TreeItem<Task> item = new MyTreeItem(extractor);
item.setValue(task);
for (Task subTask : task.getTasks()) {
TreeItem<Task> subItem = new MyTreeItem(extractor);
subItem.setValue(subTask);
item.getChildren().add(subItem);
}
rootItem.getChildren().add(item);
}
TreeView<Task> treeView = new TreeView<>();
treeView.setRoot(rootItem);
treeView.setCellFactory(taskTreeView -> new TreeCell<Task>() {
@Override
protected void updateItem(Task item, boolean empty) {
super.updateItem(item, empty);
if (item != null && !empty) {
setText(item.getName() + " (" + item.getCashFlows().size() + ")");
} else {
setText(null);
}
}
});
Button button = new Button("Add");
button.setOnAction(e -> demoTask.getCashFlows().add(1));
VBox root = new VBox(button, treeView);
root.setSpacing(10);
root.setPadding(new Insets(10));
primaryStage.setScene(new Scene(root));
primaryStage.setTitle("TreeView Demo");
primaryStage.show();
}
class MyTreeItem<T> extends TreeItem<T> {
public MyTreeItem(Callback<T, Observable[]> extractor) {
if (extractor == null) {
throw new NullPointerException("Extractor cannot be null");
}
final InvalidationListener listener = e -> Event.fireEvent(this, new TreeModificationEvent<>(TreeItem.<T>valueChangedEvent(), this, getValue()));
valueProperty().addListener((obs, oldValue, newValue) -> {
if (oldValue != null) {
Stream.of(extractor.call(oldValue)).forEach(prop -> prop.removeListener(listener));
}
if (newValue != null) {
Stream.of(extractor.call(newValue)).forEach(prop -> prop.addListener(listener));
}
});
}
}
class Task {
StringProperty name = new SimpleStringProperty();
BooleanProperty completed = new SimpleBooleanProperty();
ObservableList<Task> tasks = FXCollections.observableArrayList();
ObservableList<Integer> cashFlows = FXCollections.observableArrayList();
public Task(String n, boolean c) {
setName(n);
setCompleted(c);
}
public String getName() {
return name.get();
}
public StringProperty nameProperty() {
return name;
}
public void setName(String name) {
this.name.set(name);
}
public boolean isCompleted() {
return completed.get();
}
public BooleanProperty completedProperty() {
return completed;
}
public void setCompleted(boolean completed) {
this.completed.set(completed);
}
public ObservableList<Task> getTasks() {
return tasks;
}
public ObservableList<Integer> getCashFlows() {
return cashFlows;
}
}
}
使用监听器方法(不推荐,保留以备记录)
import javafx.application.Application;
import javafx.beans.property.BooleanProperty;
import javafx.beans.property.SimpleBooleanProperty;
import javafx.beans.property.SimpleStringProperty;
import javafx.beans.property.StringProperty;
import javafx.collections.FXCollections;
import javafx.collections.ListChangeListener;
import javafx.collections.ObservableList;
import javafx.geometry.Insets;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.TreeCell;
import javafx.scene.control.TreeItem;
import javafx.scene.control.TreeView;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;
import java.util.Random;
public class TreeViewDemo extends Application {
public static void main(String[] args) {
launch(args);
}
private Task demoTask;
@Override
public void start(Stage primaryStage) {
// BUILD DATA
Random rnd = new Random();
ObservableList<Task> tasks = FXCollections.observableArrayList();
for (int i = 1; i < 10; i++) {
Task sub1 = new Task("Sub Task A", rnd.nextBoolean());
Task sub2 = new Task("Sub Task B", rnd.nextBoolean());
Task tsk = new Task("Task " + i, rnd.nextBoolean());
if (demoTask == null) {
tsk.setName("Demo Task");
demoTask = tsk;
}
tsk.getTasks().addAll(sub1, sub2);
tasks.addAll(tsk);
}
// BUILD TREE ITEMS
TreeItem<Task> rootItem = new TreeItem<>();
rootItem.setExpanded(true);
for (Task task : tasks) {
TreeItem<Task> item = new TreeItem(task);
for (Task subTask : task.getTasks()) {
TreeItem<Task> subItem = new TreeItem(subTask);
item.getChildren().add(subItem);
}
rootItem.getChildren().add(item);
}
TreeView<Task> treeView = new TreeView<>();
treeView.setRoot(rootItem);
treeView.setCellFactory(taskTreeView -> new MyTreeCell());
Button button = new Button("Add");
button.setOnAction(e -> demoTask.getCashFlows().add(1));
VBox root = new VBox(button, treeView);
root.setSpacing(10);
root.setPadding(new Insets(10));
primaryStage.setScene(new Scene(root));
primaryStage.setTitle("TreeView Demo");
primaryStage.show();
}
class MyTreeCell extends TreeCell<Task> {
public MyTreeCell() {
ListChangeListener<? super Integer> listener = p -> updateItem(getItem(), false);
itemProperty().addListener((obs, oldItem, newItem) -> {
if (oldItem != null) {
oldItem.getCashFlows().removeListener(listener);
}
if (newItem != null) {
newItem.getCashFlows().addListener(listener);
}
});
}
@Override
protected void updateItem(Task item, boolean empty) {
super.updateItem(item, empty);
if (item != null && !empty) {
setText(item.getName() + " (" + item.getCashFlows().size() + ")");
} else {
setText(null);
}
}
}
class Task {
StringProperty name = new SimpleStringProperty();
BooleanProperty completed = new SimpleBooleanProperty();
ObservableList<Task> tasks = FXCollections.observableArrayList();
ObservableList<Integer> cashFlows = FXCollections.observableArrayList();
public Task(String n, boolean c) {
setName(n);
setCompleted(c);
}
public String getName() {
return name.get();
}
public StringProperty nameProperty() {
return name;
}
public void setName(String name) {
this.name.set(name);
}
public boolean isCompleted() {
return completed.get();
}
public BooleanProperty completedProperty() {
return completed;
}
public void setCompleted(boolean completed) {
this.completed.set(completed);
}
public ObservableList<Task> getTasks() {
return tasks;
}
public ObservableList<Integer> getCashFlows() {
return cashFlows;
}
}
}