从 TextField 更新 TreeView 项目
Updating TreeView items from TextField
这是我的一个非常简单的例子。 (向下...)
它的作用:
TreeView is populated with three Persons, when TreeItem is selected Textfield will be populated with name of the selected person.如果用户更改名称并从文本字段(或按 Enter)失去焦点,它将更改人员的名称和 "updates" TreeView 项目的显示文本。
我的问题是这一行:
selectedItem.valueProperty().set(new Person(selectedPerson.getName(), selectedPerson.getAge()));
多亏了那一行,我才能够刷新 TreeView。没有它,我只能在例如期间刷新 treeView。调整 window 的大小(或折叠和展开根项)。
我认为这个解决方案非常愚蠢,必须有更好的编码方式。我不能每次都创建一个新的 Person 实例,这对我来说是不可接受的。
我还尝试为 treeView 触发一个事件,但这种方法会混淆焦点,而且这也是一个愚蠢的解决方案。
我还找到了这样的解决方案:
treeView.getRoot().getChildren().set(treeView.getSelectionModel().getSelectedIndex(), new TreeItem<MainAppTF.Person>(updatedPerson));
这也是一个糟糕的解决方案。
也许可以使用 属性 绑定找到解决方案,但绑定是一个 "realtime/instant" 变化,除非我使用 bind() unbind(),但也许 Binding 中有一些东西可以使用我还不熟。 (我更喜欢听众)
我的主要目标是 "commit" 我对焦点更改或关键事件的更改,并在那一刻之后立即更新 TreeView。
PS: 如果能提供真实的例子,将不胜感激
public class MainAppTF extends Application {
private TreeView<Person> treeView;
private final TreeItem<Person> rootNode = new TreeItem<Person>(new Person("Root", 0));
private TextField textField;
@Override
public void start(Stage stage) {
VBox box = new VBox();
Scene scene = new Scene(box, 400, 400);
treeView = new TreeView<Person>(rootNode);
treeView.setShowRoot(false);
rootNode.setExpanded(true);
List<TreeItem<Person>> list = new ArrayList<>();
list.add(new TreeItem<Person>(new Person("Adam", 20)));
list.add(new TreeItem<Person>(new Person("Eva", 19)));
list.add(new TreeItem<Person>(new Person("Carl", 30)));
rootNode.getChildren().setAll(list);
textField = new TextField("");
attachListeners();
box.getChildren().add(treeView);
box.getChildren().add(textField);
VBox.setMargin(treeView, new Insets(10));
VBox.setMargin(textField, new Insets(10));
stage.setScene(scene);
stage.show();
}
private void attachListeners() {
treeView.getSelectionModel().selectedItemProperty().addListener(new ChangeListener<TreeItem<Person>>() {
@Override
public void changed(ObservableValue<? extends TreeItem<Person>> observable, TreeItem<Person> oldValue, TreeItem<Person> newValue) {
textField.setText(newValue.getValue().getName());
}
});
textField.focusedProperty().addListener(new ChangeListener<Boolean>() {
@Override
public void changed(ObservableValue<? extends Boolean> observable, Boolean oldValue, Boolean newValue) {
if (!newValue) {
updateTreeViewItem();
}
}
});
textField.setOnAction(new EventHandler<ActionEvent>() {
@Override
public void handle(ActionEvent event) {
updateTreeViewItem();
}
});
}
private void updateTreeViewItem() {
TreeItem<Person> selectedItem = treeView.getSelectionModel().getSelectedItem();
Person selectedPerson = selectedItem.getValue();
selectedPerson.nameProperty().set(textField.getText());
// FIXME This is silly! There must be another way!
selectedItem.valueProperty().set(new Person(selectedPerson.getName(), selectedPerson.getAge()));
}
public static void main(String[] args) {
Application.launch(args);
}
private class Person {
private StringProperty name;
private int age;
public Person() {
this(null, 0);
}
public Person(String name, int age) {
this.name = new SimpleStringProperty(name);
this.age = age;
}
public StringProperty nameProperty() {
return name;
}
public String getName() {
return name.getValue();
}
public void setName(String name) {
this.name.setValue(name);
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
@Override
public String toString() {
return getName() + " - " + getAge();
}
}
}
您希望 TreeItem
在 TreeItem
包装的 Person
的名称更改时接收 TreeModificationEvent
s。
您可以通过将侦听器附加到此人的 nameProperty()
,然后触发适当的事件来执行此操作:
TreeItem<Person> treeItem = new TreeItem<>(person);
ChangeListener<String> nameListener = (obs, oldName, newName) -> {
TreeModificationEvent<Person> event = new TreeModificationEvent<>(TreeItem.valueChangedEvent(), treeItem);
Event.fireEvent(treeItem, event);
};
person.nameProperty().addListener(nameListener);
如果您有可能更改 TreeItem
(treeItem.setValue(new Person(...))
) 包装的值,那么您需要确保从 old person 中移除监听器并将其添加到新的一个。因此,谨慎的做法也可能是:
treeItem.valueProperty().addListener((obs, oldValue, newValue) -> {
if (oldValue != null) {
oldValue.nameProperty().removeListener(nameListener);
}
if (newValue != null) {
newValue.nameProperty().addListener(nameListener);
}
});
显然,您不想每次都重复这段代码,因此您可以创建一个实用方法:
private TreeItem<Person> createTreeItem(Person person) {
TreeItem<Person> treeItem = new TreeItem<>(person);
ChangeListener<String> nameListener = (obs, oldName, newName) -> {
TreeModificationEvent<Person> event = new TreeModificationEvent<>(TreeItem.valueChangedEvent(), treeItem);
Event.fireEvent(treeItem, event);
};
person.nameProperty().addListener(nameListener);
treeItem.valueProperty().addListener((obs, oldValue, newValue) -> {
if (oldValue != null) {
oldValue.nameProperty().removeListener(nameListener);
}
if (newValue != null) {
newValue.nameProperty().addListener(nameListener);
}
});
return treeItem ;
}
然后
list.add(createTreeItem(new Person("Adam", 20)));
list.add(createTreeItem(new Person("Eva", 19)));
list.add(createTreeItem(new Person("Carl", 30)));
或者您可以创建 TreeItem<Person>
的子类:
private class PersonTreeItem extends TreeItem<Person> {
private ChangeListener<String> nameListener = (obs, oldName, newName) -> {
TreeModificationEvent<Person> event = new TreeModificationEvent<>(TreeItem.valueChangedEvent(), this);
Event.fireEvent(this, event);
};
public PersonTreeItem(Person person) {
super(person);
person.nameProperty().addListener(nameListener);
this.valueProperty().addListener((obs, oldValue, newValue) -> {
if (oldValue != null) {
oldValue.nameProperty().removeListener(nameListener);
}
if (newValue != null) {
newValue.nameProperty().addListener(nameListener);
}
});
}
}
并做
list.add(new PersonTreeItem(new Person("Adam", 20)));
list.add(new PersonTreeItem(new Person("Eva", 19)));
list.add(new PersonTreeItem(new Person("Carl", 30)));
(两者之间的选择本质上只是风格问题。)
SSCCE:
import java.util.ArrayList;
import java.util.List;
import javafx.application.Application;
import javafx.beans.property.SimpleStringProperty;
import javafx.beans.property.StringProperty;
import javafx.beans.value.ChangeListener;
import javafx.beans.value.ObservableValue;
import javafx.event.ActionEvent;
import javafx.event.Event;
import javafx.event.EventHandler;
import javafx.geometry.Insets;
import javafx.scene.Scene;
import javafx.scene.control.TextField;
import javafx.scene.control.TreeItem;
import javafx.scene.control.TreeItem.TreeModificationEvent;
import javafx.scene.control.TreeView;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;
public class MainAppTF extends Application {
private TreeView<Person> treeView;
private final TreeItem<Person> rootNode = new TreeItem<Person>(new Person("Root", 0));
private TextField textField;
@Override
public void start(Stage stage) {
VBox box = new VBox();
Scene scene = new Scene(box, 400, 400);
treeView = new TreeView<Person>(rootNode);
treeView.setShowRoot(false);
rootNode.setExpanded(true);
List<TreeItem<Person>> list = new ArrayList<>();
list.add(createTreeItem(new Person("Adam", 20)));
list.add(createTreeItem(new Person("Eva", 19)));
list.add(createTreeItem(new Person("Carl", 30)));
rootNode.getChildren().setAll(list);
textField = new TextField("");
attachListeners();
box.getChildren().add(treeView);
box.getChildren().add(textField);
VBox.setMargin(treeView, new Insets(10));
VBox.setMargin(textField, new Insets(10));
stage.setScene(scene);
stage.show();
}
private void attachListeners() {
treeView.getSelectionModel().selectedItemProperty().addListener(new ChangeListener<TreeItem<Person>>() {
@Override
public void changed(ObservableValue<? extends TreeItem<Person>> observable, TreeItem<Person> oldValue, TreeItem<Person> newValue) {
textField.setText(newValue.getValue().getName());
}
});
textField.focusedProperty().addListener(new ChangeListener<Boolean>() {
@Override
public void changed(ObservableValue<? extends Boolean> observable, Boolean oldValue, Boolean newValue) {
if (!newValue) {
updateTreeViewItem();
}
}
});
textField.setOnAction(new EventHandler<ActionEvent>() {
@Override
public void handle(ActionEvent event) {
updateTreeViewItem();
}
});
}
private void updateTreeViewItem() {
TreeItem<Person> selectedItem = treeView.getSelectionModel().getSelectedItem();
Person selectedPerson = selectedItem.getValue();
selectedPerson.nameProperty().set(textField.getText());
// FIXME This is silly! There must be another way!
// selectedItem.valueProperty().set(new Person(selectedPerson.getName(), selectedPerson.getAge()));
}
public static void main(String[] args) {
Application.launch(args);
}
private TreeItem<Person> createTreeItem(Person person) {
TreeItem<Person> treeItem = new TreeItem<>(person);
ChangeListener<String> nameListener = (obs, oldName, newName) -> {
TreeModificationEvent<Person> event = new TreeModificationEvent<>(TreeItem.valueChangedEvent(), treeItem);
Event.fireEvent(treeItem, event);
};
person.nameProperty().addListener(nameListener);
treeItem.valueProperty().addListener((obs, oldValue, newValue) -> {
if (oldValue != null) {
oldValue.nameProperty().removeListener(nameListener);
}
if (newValue != null) {
newValue.nameProperty().addListener(nameListener);
}
});
return treeItem ;
}
private class Person {
private StringProperty name;
private int age;
public Person() {
this(null, 0);
}
public Person(String name, int age) {
this.name = new SimpleStringProperty(name);
this.age = age;
}
public StringProperty nameProperty() {
return name;
}
public String getName() {
return name.getValue();
}
public void setName(String name) {
this.name.setValue(name);
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
@Override
public String toString() {
return getName() + " - " + getAge();
}
}
}
这是我的一个非常简单的例子。 (向下...)
它的作用: TreeView is populated with three Persons, when TreeItem is selected Textfield will be populated with name of the selected person.如果用户更改名称并从文本字段(或按 Enter)失去焦点,它将更改人员的名称和 "updates" TreeView 项目的显示文本。
我的问题是这一行:
selectedItem.valueProperty().set(new Person(selectedPerson.getName(), selectedPerson.getAge()));
多亏了那一行,我才能够刷新 TreeView。没有它,我只能在例如期间刷新 treeView。调整 window 的大小(或折叠和展开根项)。
我认为这个解决方案非常愚蠢,必须有更好的编码方式。我不能每次都创建一个新的 Person 实例,这对我来说是不可接受的。
我还尝试为 treeView 触发一个事件,但这种方法会混淆焦点,而且这也是一个愚蠢的解决方案。 我还找到了这样的解决方案:
treeView.getRoot().getChildren().set(treeView.getSelectionModel().getSelectedIndex(), new TreeItem<MainAppTF.Person>(updatedPerson));
这也是一个糟糕的解决方案。
也许可以使用 属性 绑定找到解决方案,但绑定是一个 "realtime/instant" 变化,除非我使用 bind() unbind(),但也许 Binding 中有一些东西可以使用我还不熟。 (我更喜欢听众)
我的主要目标是 "commit" 我对焦点更改或关键事件的更改,并在那一刻之后立即更新 TreeView。
PS: 如果能提供真实的例子,将不胜感激
public class MainAppTF extends Application {
private TreeView<Person> treeView;
private final TreeItem<Person> rootNode = new TreeItem<Person>(new Person("Root", 0));
private TextField textField;
@Override
public void start(Stage stage) {
VBox box = new VBox();
Scene scene = new Scene(box, 400, 400);
treeView = new TreeView<Person>(rootNode);
treeView.setShowRoot(false);
rootNode.setExpanded(true);
List<TreeItem<Person>> list = new ArrayList<>();
list.add(new TreeItem<Person>(new Person("Adam", 20)));
list.add(new TreeItem<Person>(new Person("Eva", 19)));
list.add(new TreeItem<Person>(new Person("Carl", 30)));
rootNode.getChildren().setAll(list);
textField = new TextField("");
attachListeners();
box.getChildren().add(treeView);
box.getChildren().add(textField);
VBox.setMargin(treeView, new Insets(10));
VBox.setMargin(textField, new Insets(10));
stage.setScene(scene);
stage.show();
}
private void attachListeners() {
treeView.getSelectionModel().selectedItemProperty().addListener(new ChangeListener<TreeItem<Person>>() {
@Override
public void changed(ObservableValue<? extends TreeItem<Person>> observable, TreeItem<Person> oldValue, TreeItem<Person> newValue) {
textField.setText(newValue.getValue().getName());
}
});
textField.focusedProperty().addListener(new ChangeListener<Boolean>() {
@Override
public void changed(ObservableValue<? extends Boolean> observable, Boolean oldValue, Boolean newValue) {
if (!newValue) {
updateTreeViewItem();
}
}
});
textField.setOnAction(new EventHandler<ActionEvent>() {
@Override
public void handle(ActionEvent event) {
updateTreeViewItem();
}
});
}
private void updateTreeViewItem() {
TreeItem<Person> selectedItem = treeView.getSelectionModel().getSelectedItem();
Person selectedPerson = selectedItem.getValue();
selectedPerson.nameProperty().set(textField.getText());
// FIXME This is silly! There must be another way!
selectedItem.valueProperty().set(new Person(selectedPerson.getName(), selectedPerson.getAge()));
}
public static void main(String[] args) {
Application.launch(args);
}
private class Person {
private StringProperty name;
private int age;
public Person() {
this(null, 0);
}
public Person(String name, int age) {
this.name = new SimpleStringProperty(name);
this.age = age;
}
public StringProperty nameProperty() {
return name;
}
public String getName() {
return name.getValue();
}
public void setName(String name) {
this.name.setValue(name);
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
@Override
public String toString() {
return getName() + " - " + getAge();
}
}
}
您希望 TreeItem
在 TreeItem
包装的 Person
的名称更改时接收 TreeModificationEvent
s。
您可以通过将侦听器附加到此人的 nameProperty()
,然后触发适当的事件来执行此操作:
TreeItem<Person> treeItem = new TreeItem<>(person);
ChangeListener<String> nameListener = (obs, oldName, newName) -> {
TreeModificationEvent<Person> event = new TreeModificationEvent<>(TreeItem.valueChangedEvent(), treeItem);
Event.fireEvent(treeItem, event);
};
person.nameProperty().addListener(nameListener);
如果您有可能更改 TreeItem
(treeItem.setValue(new Person(...))
) 包装的值,那么您需要确保从 old person 中移除监听器并将其添加到新的一个。因此,谨慎的做法也可能是:
treeItem.valueProperty().addListener((obs, oldValue, newValue) -> {
if (oldValue != null) {
oldValue.nameProperty().removeListener(nameListener);
}
if (newValue != null) {
newValue.nameProperty().addListener(nameListener);
}
});
显然,您不想每次都重复这段代码,因此您可以创建一个实用方法:
private TreeItem<Person> createTreeItem(Person person) {
TreeItem<Person> treeItem = new TreeItem<>(person);
ChangeListener<String> nameListener = (obs, oldName, newName) -> {
TreeModificationEvent<Person> event = new TreeModificationEvent<>(TreeItem.valueChangedEvent(), treeItem);
Event.fireEvent(treeItem, event);
};
person.nameProperty().addListener(nameListener);
treeItem.valueProperty().addListener((obs, oldValue, newValue) -> {
if (oldValue != null) {
oldValue.nameProperty().removeListener(nameListener);
}
if (newValue != null) {
newValue.nameProperty().addListener(nameListener);
}
});
return treeItem ;
}
然后
list.add(createTreeItem(new Person("Adam", 20)));
list.add(createTreeItem(new Person("Eva", 19)));
list.add(createTreeItem(new Person("Carl", 30)));
或者您可以创建 TreeItem<Person>
的子类:
private class PersonTreeItem extends TreeItem<Person> {
private ChangeListener<String> nameListener = (obs, oldName, newName) -> {
TreeModificationEvent<Person> event = new TreeModificationEvent<>(TreeItem.valueChangedEvent(), this);
Event.fireEvent(this, event);
};
public PersonTreeItem(Person person) {
super(person);
person.nameProperty().addListener(nameListener);
this.valueProperty().addListener((obs, oldValue, newValue) -> {
if (oldValue != null) {
oldValue.nameProperty().removeListener(nameListener);
}
if (newValue != null) {
newValue.nameProperty().addListener(nameListener);
}
});
}
}
并做
list.add(new PersonTreeItem(new Person("Adam", 20)));
list.add(new PersonTreeItem(new Person("Eva", 19)));
list.add(new PersonTreeItem(new Person("Carl", 30)));
(两者之间的选择本质上只是风格问题。)
SSCCE:
import java.util.ArrayList;
import java.util.List;
import javafx.application.Application;
import javafx.beans.property.SimpleStringProperty;
import javafx.beans.property.StringProperty;
import javafx.beans.value.ChangeListener;
import javafx.beans.value.ObservableValue;
import javafx.event.ActionEvent;
import javafx.event.Event;
import javafx.event.EventHandler;
import javafx.geometry.Insets;
import javafx.scene.Scene;
import javafx.scene.control.TextField;
import javafx.scene.control.TreeItem;
import javafx.scene.control.TreeItem.TreeModificationEvent;
import javafx.scene.control.TreeView;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;
public class MainAppTF extends Application {
private TreeView<Person> treeView;
private final TreeItem<Person> rootNode = new TreeItem<Person>(new Person("Root", 0));
private TextField textField;
@Override
public void start(Stage stage) {
VBox box = new VBox();
Scene scene = new Scene(box, 400, 400);
treeView = new TreeView<Person>(rootNode);
treeView.setShowRoot(false);
rootNode.setExpanded(true);
List<TreeItem<Person>> list = new ArrayList<>();
list.add(createTreeItem(new Person("Adam", 20)));
list.add(createTreeItem(new Person("Eva", 19)));
list.add(createTreeItem(new Person("Carl", 30)));
rootNode.getChildren().setAll(list);
textField = new TextField("");
attachListeners();
box.getChildren().add(treeView);
box.getChildren().add(textField);
VBox.setMargin(treeView, new Insets(10));
VBox.setMargin(textField, new Insets(10));
stage.setScene(scene);
stage.show();
}
private void attachListeners() {
treeView.getSelectionModel().selectedItemProperty().addListener(new ChangeListener<TreeItem<Person>>() {
@Override
public void changed(ObservableValue<? extends TreeItem<Person>> observable, TreeItem<Person> oldValue, TreeItem<Person> newValue) {
textField.setText(newValue.getValue().getName());
}
});
textField.focusedProperty().addListener(new ChangeListener<Boolean>() {
@Override
public void changed(ObservableValue<? extends Boolean> observable, Boolean oldValue, Boolean newValue) {
if (!newValue) {
updateTreeViewItem();
}
}
});
textField.setOnAction(new EventHandler<ActionEvent>() {
@Override
public void handle(ActionEvent event) {
updateTreeViewItem();
}
});
}
private void updateTreeViewItem() {
TreeItem<Person> selectedItem = treeView.getSelectionModel().getSelectedItem();
Person selectedPerson = selectedItem.getValue();
selectedPerson.nameProperty().set(textField.getText());
// FIXME This is silly! There must be another way!
// selectedItem.valueProperty().set(new Person(selectedPerson.getName(), selectedPerson.getAge()));
}
public static void main(String[] args) {
Application.launch(args);
}
private TreeItem<Person> createTreeItem(Person person) {
TreeItem<Person> treeItem = new TreeItem<>(person);
ChangeListener<String> nameListener = (obs, oldName, newName) -> {
TreeModificationEvent<Person> event = new TreeModificationEvent<>(TreeItem.valueChangedEvent(), treeItem);
Event.fireEvent(treeItem, event);
};
person.nameProperty().addListener(nameListener);
treeItem.valueProperty().addListener((obs, oldValue, newValue) -> {
if (oldValue != null) {
oldValue.nameProperty().removeListener(nameListener);
}
if (newValue != null) {
newValue.nameProperty().addListener(nameListener);
}
});
return treeItem ;
}
private class Person {
private StringProperty name;
private int age;
public Person() {
this(null, 0);
}
public Person(String name, int age) {
this.name = new SimpleStringProperty(name);
this.age = age;
}
public StringProperty nameProperty() {
return name;
}
public String getName() {
return name.getValue();
}
public void setName(String name) {
this.name.setValue(name);
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
@Override
public String toString() {
return getName() + " - " + getAge();
}
}
}