2015-09-09 76 views
1

这是我非常简单的例子。 (下......)从TextField更新TreeView项目

做些什么: TreeView控件填充了三个人,当TreeItem选择文本字段将与选定人员的名称来填充。如果用户改变了名字并且失去了文本框的焦点(或者按下了回车键),它将改变人物的名字和“更新”TreeView项目的显示文字。

我的问题是这一行:

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

只是由于该行我能刷新树视图。没有这个,我只能在例如调整窗口大小(或折叠根项目的扩展)。

我认为这个解决方案是非常愚蠢的,必须有一个办法如何把它更好的代码。我无法每次创建Person的新实例,这对我来说是不可接受的。

我也试图火树视图的事件,但这种方法与重点弄乱了,它也是一个愚蠢的解决方案了。 我也发现了这样的解决方案:

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

这是不好解决了。除非我玩bind()unbind(),但也许在Binding中有一些我不熟悉的东西,但绑定是一种“实时/即时”的改变然而。 (我喜欢听众)

我的主要目标是,片刻后有权“承诺”我的焦点变化或关键事件的变化和更新的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(); 
     } 

    } 

} 
+0

【相关问题(HTTP://计算器。 com/q/30556259/203657),它基本上是以自定义的TreeItem实现开始的(@James_D概述的选项之一) – kleopatra

回答

3

您希望TreeItem接收TreeModificationEventsPerson的名称由TreeItem变化包裹。

您可以通过一个倾听者连接到人的nameProperty(),然后烧制相应的事件做到这一点:如果你改变由TreeItemtreeItem.setValue(new 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); 
    } 
}); 

很显然,你不想每次都重复这个代码,这样你就可以创建一个工具方法:

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

    } 

} 
+0

Thanks @James_D!有用!在TreeItem创建期间添加监听器是个好主意。感谢这种方法,我不需要创建它们,并在开始时将它们分配给所有属性,但像您一样懒惰地分配它们。 – Srnec