从TextField更新TreeView项

时间:2015-09-09 11:31:12

标签: javafx treeview javafx-8

这是我非常简单的例子。 (向下...)

它的作用: TreeView将填充三个人,当选择TreeItem时,Textfield将填充所选人员的姓名。如果用户更改了名称并从文本字段中失去焦点(或按Enter键),则会更改人员姓名和"更新" TreeView项目的显示文本。

我的问题在于这一行:

selectedItem.valueProperty().set(new Person(selectedPerson.getName(), selectedPerson.getAge()));

只有这一行,我才能刷新TreeView。没有它,我只能在例如期间刷新树视图。调整窗口大小(或折叠和扩展根项目)。

我认为这个解决方案非常愚蠢,必须有一种方法可以更好地编写代码。我不能每次都创建一个新的Person实例,这对我来说是不可接受的。

我也尝试为treeView触发事件,但这种方法与焦点混淆,它也是一个愚蠢的解决方案。 我也找到了这样的解决方案:

treeView.getRoot().getChildren().set(treeView.getSelectionModel().getSelectedIndex(), new TreeItem<MainAppTF.Person>(updatedPerson));

这也是一个不好的解决方案。

也许可以使用属性绑定找到解决方案,但绑定是&#34;实时/即时&#34;更改,除非我使用bind()unbind(),但也许在Binding中有一些我不熟悉的东西。 (我更喜欢听众)

我的主要目标是&#34;提交&#34;我对焦点更改或键事件的更改,并在那之后立即更新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();
        }

    }

}

1 个答案:

答案 0 :(得分:4)

NOTE: Use Firefox browser to see the issue.<br> <div class="wrapper"> <a href="" class="button">I'm a button</a> <input type="submit" class="button buttonfix" value="I'm a button"> <input type="submit" class="button" value="I'm a button"> </div> Notice last button has extra height forcing the container to show top/bottom of other buttons <br> <br>Input Button - Fixed<br> <input type="submit" class="button buttonfix" value="I'm a much longer button"> <br>A Tag - fine<br> <a href="" class="button">I'm a much longer button</a> <br>Input button - bug?<br> <input type="submit" class="button" value="I'm a much longer button">包裹的TreeItem的名称发生变化时,您希望Person收到TreeModificationEvents

您可以通过将监听器附加到此人的TreeItem,然后触发相应的事件来执行此操作:

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(...))

显然,您不希望每次都重复此代码,因此您可以创建实用程序方法:

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);
            }
        });
    }
}

(两者之间的选择基本上只是风格问题。)

SSCCE:

list.add(new PersonTreeItem(new Person("Adam", 20)));
list.add(new PersonTreeItem(new Person("Eva", 19)));
list.add(new PersonTreeItem(new Person("Carl", 30)));