2015-09-09 119 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