JavaFX UI控件教程(十四)之Tree View
翻譯自? ?Tree View
在本章中,您將學習如何在JavaFX應用程序中構建樹結構,向樹視圖添加項,處理事件以及通過實現和應用單元工廠來自定義樹單元。
包的TreeView類javafx.scene.control提供了層次結構的視圖。在每個樹中,層次結構中的最高對象稱為“根”。根包含幾個子項,也可以有子項。沒有孩子的項目稱為“葉子”。
圖13-1顯示了具有樹視圖的應用程序的屏幕截圖。
圖13-1樹視圖示例
?
創建樹視圖
在JavaFX應用程序中構建樹結構時,通常需要實例化TreeView類,定義多個TreeItem對象,使其中一個樹項成為根,將根添加到樹視圖中,將其他樹項添加到根中。
您可以使用相應的TreeItem類構造函數或通過調用setGraphic方法,使用圖形圖標來附加每個樹項。圖標的建議大小為16x16,但事實上,任何Node對象都可以設置為圖標,并且它將是完全交互式的。
例13-1是具有根和五個葉子的簡單樹視圖的實現。
示例13-1創建樹視圖
import javafx.application.Application; import javafx.scene.Node; import javafx.scene.Scene; import javafx.scene.control.TreeItem; import javafx.scene.control.TreeView; import javafx.scene.image.Image; import javafx.scene.image.ImageView; import javafx.scene.layout.StackPane; import javafx.stage.Stage;public class TreeViewSample extends Application {private final Node rootIcon = new ImageView(new Image(getClass().getResourceAsStream("folder_16.png")));public static void main(String[] args) {launch(args);}@Overridepublic void start(Stage primaryStage) {primaryStage.setTitle("Tree View Sample"); TreeItem<String> rootItem = new TreeItem<String> ("Inbox", rootIcon);rootItem.setExpanded(true);for (int i = 1; i < 6; i++) {TreeItem<String> item = new TreeItem<String> ("Message" + i); rootItem.getChildren().add(item);} TreeView<String> tree = new TreeView<String> (rootItem); StackPane root = new StackPane();root.getChildren().add(tree);primaryStage.setScene(new Scene(root, 300, 250));primaryStage.show();} }for通過調用getChildren和add方法將循環中創建的所有樹項添加到根項。您也可以使用該addAll方法而不是add方法一次包含所有先前創建的樹項。
如示例13-1所示,您可以在TreeView創建新TreeView對象時在類的構造函數中指定樹的根,也可以通過調用類的方法來設置它。setRootTreeView
setExpanded調用根項的方法定義了樹視圖項的初始外觀。默認情況下,所有TreeItem實例都已折疊,必要時必須手動展開。將true值傳遞給setExpanded方法,以便在應用程序啟動時根樹項看起來展開,如圖13-2所示。
圖13-2具有五個樹項的樹視圖
例13-1創建了一個包含String項目的簡單樹視圖。但是,樹結構可以包含不同類型的項目。使用以下TreeItem構造函數的通用表示法來定義由樹項表示的特定于應用程序的數據:TreeItem<T> (T value)。該T值可以指定任何對象,例如UI控件或自定義組件。
與TreeView類不同,TreeItem該類不擴展Node類。因此,您無法應用任何視覺效果或向樹項目添加菜單。使用單元工廠機制克服此障礙,并根據應用程序的需要為樹項定義盡可能多的自定義行為。
?
實現Cell工廠
細胞工廠機制被用于生成TreeCell實例來表示單個TreeItem的TreeView。當您的應用程序使用動態更改或按需添加的過多數據進行操作時,使用單元工廠尤其有用。
考慮一個可視化給定公司的人力資源數據的應用程序,并使用戶能夠修改員工詳細信息并添加新員工。
例13-2創建了Employee類,并根據各自的部門安排員工。
示例13-2創建人力資源樹視圖模型
import java.util.Arrays; import java.util.List; import javafx.application.Application; import javafx.scene.Node; import javafx.scene.Scene; import javafx.scene.control.TreeItem; import javafx.scene.control.TreeView; import javafx.scene.image.Image; import javafx.scene.image.ImageView; import javafx.scene.paint.Color; import javafx.stage.Stage;import javafx.beans.property.SimpleStringProperty; import javafx.scene.layout.VBox;public class TreeViewSample extends Application {private final Node rootIcon = new ImageView(new Image(getClass().getResourceAsStream("root.png")));private final Image depIcon = new Image(getClass().getResourceAsStream("department.png"));List<Employee> employees = Arrays.<Employee>asList(new Employee("Ethan Williams", "Sales Department"),new Employee("Emma Jones", "Sales Department"),new Employee("Michael Brown", "Sales Department"),new Employee("Anna Black", "Sales Department"),new Employee("Rodger York", "Sales Department"),new Employee("Susan Collins", "Sales Department"),new Employee("Mike Graham", "IT Support"),new Employee("Judy Mayer", "IT Support"),new Employee("Gregory Smith", "IT Support"),new Employee("Jacob Smith", "Accounts Department"),new Employee("Isabella Johnson", "Accounts Department"));TreeItem<String> rootNode = new TreeItem<String>("MyCompany Human Resources", rootIcon);public static void main(String[] args) {launch(args);}@Overridepublic void start(Stage stage) {rootNode.setExpanded(true);for (Employee employee : employees) {TreeItem<String> empLeaf = new TreeItem<String>(employee.getName());boolean found = false;for (TreeItem<String> depNode : rootNode.getChildren()) {if (depNode.getValue().contentEquals(employee.getDepartment())){depNode.getChildren().add(empLeaf);found = true;break;}}if (!found) {TreeItem<String> depNode = new TreeItem<String>(employee.getDepartment(), new ImageView(depIcon));rootNode.getChildren().add(depNode);depNode.getChildren().add(empLeaf);}}stage.setTitle("Tree View Sample");VBox box = new VBox();final Scene scene = new Scene(box, 400, 300);scene.setFill(Color.LIGHTGRAY);TreeView<String> treeView = new TreeView<String>(rootNode);box.getChildren().add(treeView);stage.setScene(scene);stage.show();}public static class Employee {private final SimpleStringProperty name;private final SimpleStringProperty department;private Employee(String name, String department) {this.name = new SimpleStringProperty(name);this.department = new SimpleStringProperty(department);}public String getName() {return name.get();}public void setName(String fName) {name.set(fName);}public String getDepartment() {return department.get();}public void setDepartment(String fName) {department.set(fName);}} }例13-2中的每個Employee對象都有兩個屬性:和。與雇員相對應的對象被稱為樹葉,而與部門對應的樹項被稱為具有子項的樹項。通過調用方法從對象中檢索要創建的新部門的名稱。namedepartmentTreeItemEmployeegetDepartment
編譯并運行此應用程序時,它會創建如圖13-3所示的窗口。
圖13-3樹視圖示例應用程序中的員工列表
使用示例13-2,您可以預覽樹視圖及其項目,但不能更改現有項目或添加任何新項目。例13-3顯示了實現單元工廠的應用程序的修改版本。修改后的應用程序使您可以更改員工的姓名。
示例13-3實現單元工廠
import java.util.Arrays; import java.util.List; import javafx.application.Application; import javafx.event.EventHandler; import javafx.scene.Node; import javafx.scene.Scene; import javafx.scene.control.TextField; import javafx.scene.control.TreeCell; import javafx.scene.control.TreeItem; import javafx.scene.control.TreeView; import javafx.scene.image.Image; import javafx.scene.image.ImageView; import javafx.scene.input.KeyCode; import javafx.scene.input.KeyEvent; import javafx.scene.paint.Color; import javafx.stage.Stage; import javafx.util.Callback;import javafx.beans.property.SimpleStringProperty; import javafx.scene.layout.VBox;public class TreeViewSample extends Application {private final Node rootIcon = new ImageView(new Image(getClass().getResourceAsStream("root.png")));private final Image depIcon = new Image(getClass().getResourceAsStream("department.png"));List<Employee> employees = Arrays.<Employee>asList(new Employee("Ethan Williams", "Sales Department"),new Employee("Emma Jones", "Sales Department"),new Employee("Michael Brown", "Sales Department"),new Employee("Anna Black", "Sales Department"),new Employee("Rodger York", "Sales Department"),new Employee("Susan Collins", "Sales Department"),new Employee("Mike Graham", "IT Support"),new Employee("Judy Mayer", "IT Support"),new Employee("Gregory Smith", "IT Support"),new Employee("Jacob Smith", "Accounts Department"),new Employee("Isabella Johnson", "Accounts Department"));TreeItem<String> rootNode = new TreeItem<String>("MyCompany Human Resources", rootIcon);public static void main(String[] args) {Application.launch(args);}@Overridepublic void start(Stage stage) {rootNode.setExpanded(true);for (Employee employee : employees) {TreeItem<String> empLeaf = new TreeItem<String>(employee.getName());boolean found = false;for (TreeItem<String> depNode : rootNode.getChildren()) {if (depNode.getValue().contentEquals(employee.getDepartment())){depNode.getChildren().add(empLeaf);found = true;break;}}if (!found) {TreeItem<String> depNode = new TreeItem<String>(employee.getDepartment(), new ImageView(depIcon));rootNode.getChildren().add(depNode);depNode.getChildren().add(empLeaf);}}stage.setTitle("Tree View Sample");VBox box = new VBox();final Scene scene = new Scene(box, 400, 300);scene.setFill(Color.LIGHTGRAY);TreeView<String> treeView = new TreeView<String>(rootNode);treeView.setEditable(true);treeView.setCellFactory(new Callback<TreeView<String>,TreeCell<String>>(){@Overridepublic TreeCell<String> call(TreeView<String> p) {return new TextFieldTreeCellImpl();}});box.getChildren().add(treeView);stage.setScene(scene);stage.show();}private final class TextFieldTreeCellImpl extends TreeCell<String> {private TextField textField;public TextFieldTreeCellImpl() {}@Overridepublic void startEdit() {super.startEdit();if (textField == null) {createTextField();}setText(null);setGraphic(textField);textField.selectAll();}@Overridepublic void cancelEdit() {super.cancelEdit();setText((String) getItem());setGraphic(getTreeItem().getGraphic());}@Overridepublic void updateItem(String item, boolean empty) {super.updateItem(item, empty);if (empty) {setText(null);setGraphic(null);} else {if (isEditing()) {if (textField != null) {textField.setText(getString());}setText(null);setGraphic(textField);} else {setText(getString());setGraphic(getTreeItem().getGraphic());}}}private void createTextField() {textField = new TextField(getString());textField.setOnKeyReleased(new EventHandler<KeyEvent>() {@Overridepublic void handle(KeyEvent t) {if (t.getCode() == KeyCode.ENTER) {commitEdit(textField.getText());} else if (t.getCode() == KeyCode.ESCAPE) {cancelEdit();}}});}private String getString() {return getItem() == null ? "" : getItem().toString();}}public static class Employee {private final SimpleStringProperty name;private final SimpleStringProperty department;private Employee(String name, String department) {this.name = new SimpleStringProperty(name);this.department = new SimpleStringProperty(department);}public String getName() {return name.get();}public void setName(String fName) {name.set(fName);}public String getDepartment() {return department.get();}public void setDepartment(String fName) {department.set(fName);}} }setCellFactory調用該treeView對象的方法會覆蓋TreeCell實現并重新定義TextFieldTreeCellImpl類中指定的樹項。
在TextFieldTreeCellImpl類創建了一個TextField針對每個樹對象,并提供方法來處理編輯事件。
請注意,必須setEditable(true)在樹視圖上顯式調用該方法才能編輯其所有項目。
編譯并運行例13-3中的應用程序。然后嘗試單擊樹中的員工并更改其名稱。圖13-4顯示了在IT支持部門編輯樹項目的時刻。
圖13-4更改員工姓名
?
按需添加新樹項
修改Tree View Sample應用程序,以便人力資源代表可以添加新員工。使用示例13-4的粗體代碼行作為參考。這些行將上下文菜單添加到與部門對應的樹項目中。選擇“添加員工”菜單項后,新樹項將作為葉添加到當前部門。
使用此isLeaf方法可以區分部門樹項和員工樹項。
示例13-4添加新樹項
import java.util.Arrays; import java.util.List; import javafx.application.Application; import javafx.event.Event; import javafx.event.EventHandler; import javafx.scene.Node; import javafx.scene.Scene; import javafx.scene.control.TextField; import javafx.scene.control.TreeCell; import javafx.scene.control.TreeItem; import javafx.scene.control.TreeView; import javafx.scene.image.Image; import javafx.scene.image.ImageView; import javafx.scene.input.KeyCode; import javafx.scene.input.KeyEvent; import javafx.scene.paint.Color; import javafx.stage.Stage; import javafx.util.Callback;import javafx.beans.property.SimpleStringProperty; import javafx.scene.control.ContextMenu; import javafx.scene.control.MenuItem; import javafx.scene.layout.VBox;public class TreeViewSample extends Application {private final Node rootIcon = new ImageView(new Image(getClass().getResourceAsStream("root.png")));private final Image depIcon = new Image(getClass().getResourceAsStream("department.png"));List<Employee> employees = Arrays.<Employee>asList(new Employee("Ethan Williams", "Sales Department"),new Employee("Emma Jones", "Sales Department"),new Employee("Michael Brown", "Sales Department"),new Employee("Anna Black", "Sales Department"),new Employee("Rodger York", "Sales Department"),new Employee("Susan Collins", "Sales Department"),new Employee("Mike Graham", "IT Support"),new Employee("Judy Mayer", "IT Support"),new Employee("Gregory Smith", "IT Support"),new Employee("Jacob Smith", "Accounts Department"),new Employee("Isabella Johnson", "Accounts Department"));TreeItem<String> rootNode = new TreeItem<String>("MyCompany Human Resources", rootIcon);public static void main(String[] args) {Application.launch(args);}@Overridepublic void start(Stage stage) {rootNode.setExpanded(true);for (Employee employee : employees) {TreeItem<String> empLeaf = new TreeItem<String>(employee.getName());boolean found = false;for (TreeItem<String> depNode : rootNode.getChildren()) {if (depNode.getValue().contentEquals(employee.getDepartment())){depNode.getChildren().add(empLeaf);found = true;break;}}if (!found) {TreeItem depNode = new TreeItem(employee.getDepartment(), new ImageView(depIcon));rootNode.getChildren().add(depNode);depNode.getChildren().add(empLeaf);}}stage.setTitle("Tree View Sample");VBox box = new VBox();final Scene scene = new Scene(box, 400, 300);scene.setFill(Color.LIGHTGRAY);TreeView<String> treeView = new TreeView<String>(rootNode);treeView.setEditable(true);treeView.setCellFactory(new Callback<TreeView<String>,TreeCell<String>>(){@Overridepublic TreeCell<String> call(TreeView<String> p) {return new TextFieldTreeCellImpl();}});box.getChildren().add(treeView);stage.setScene(scene);stage.show();}private final class TextFieldTreeCellImpl extends TreeCell<String> {private TextField textField;private ContextMenu addMenu = new ContextMenu();public TextFieldTreeCellImpl() {MenuItem addMenuItem = new MenuItem("Add Employee");addMenu.getItems().add(addMenuItem);addMenuItem.setOnAction(new EventHandler() {public void handle(Event t) {TreeItem newEmployee = new TreeItem<String>("New Employee");getTreeItem().getChildren().add(newEmployee);}});}@Overridepublic void startEdit() {super.startEdit();if (textField == null) {createTextField();}setText(null);setGraphic(textField);textField.selectAll();}@Overridepublic void cancelEdit() {super.cancelEdit();setText((String) getItem());setGraphic(getTreeItem().getGraphic());}@Overridepublic void updateItem(String item, boolean empty) {super.updateItem(item, empty);if (empty) {setText(null);setGraphic(null);} else {if (isEditing()) {if (textField != null) {textField.setText(getString());}setText(null);setGraphic(textField);} else {setText(getString());setGraphic(getTreeItem().getGraphic());if (!getTreeItem().isLeaf()&&getTreeItem().getParent()!= null){setContextMenu(addMenu);}}}}private void createTextField() {textField = new TextField(getString());textField.setOnKeyReleased(new EventHandler<KeyEvent>() {@Overridepublic void handle(KeyEvent t) {if (t.getCode() == KeyCode.ENTER) {commitEdit(textField.getText());} else if (t.getCode() == KeyCode.ESCAPE) {cancelEdit();}}}); }private String getString() {return getItem() == null ? "" : getItem().toString();}}public static class Employee {private final SimpleStringProperty name;private final SimpleStringProperty department;private Employee(String name, String department) {this.name = new SimpleStringProperty(name);this.department = new SimpleStringProperty(department);}public String getName() {return name.get();}public void setName(String fName) {name.set(fName);}public String getDepartment() {return department.get();}public void setDepartment(String fName) {department.set(fName);}} }編譯并運行應用程序。然后在樹結構中選擇一個部門并右鍵單擊它。出現上下文菜單,如圖13-5所示。
圖13-5添加新員工的上下文菜單
從上下文菜單中選擇“添加員工”菜單項時,新記錄將添加到當前部門。圖13-6顯示了添加到Accounts Department的新樹項。
圖13-6新增員工
由于已為樹項啟用了編輯,因此可以將默認的“新員工”值更改為相應的名稱。
?
使用樹單元格編輯器
開始的JavaFX SDK 2.2,可以使用可用下面的樹單元格編輯器的API中:CheckBoxTreeCell,ChoiceBoxTreeCell,ComboBoxTreeCell,TextFieldTreeCell。類擴展了TreeCell實現以在單元內呈現特定控件。
例13-5演示了CheckBoxTreeCell在UI中使用類來構建復選框的層次結構。
示例13-5使用CheckBoxTreeCell類
import javafx.application.Application; import javafx.scene.Scene; import javafx.scene.control.*; import javafx.scene.control.cell.CheckBoxTreeCell; import javafx.scene.layout.StackPane; import javafx.stage.Stage;public class TreeViewSample extends Application {public static void main(String[] args) {launch(args);}@Overridepublic void start(Stage primaryStage) {primaryStage.setTitle("Tree View Sample"); CheckBoxTreeItem<String> rootItem = new CheckBoxTreeItem<String>("View Source Files");rootItem.setExpanded(true); final TreeView tree = new TreeView(rootItem); tree.setEditable(true);tree.setCellFactory(CheckBoxTreeCell.<String>forTreeView()); for (int i = 0; i < 8; i++) {final CheckBoxTreeItem<String> checkBoxTreeItem = new CheckBoxTreeItem<String>("Sample" + (i+1));rootItem.getChildren().add(checkBoxTreeItem); }tree.setRoot(rootItem);tree.setShowRoot(true);StackPane root = new StackPane();root.getChildren().add(tree);primaryStage.setScene(new Scene(root, 300, 250));primaryStage.show();} }例13-5使用CheckBoxTreeItem類而不是TreeItem?構建樹視圖。該CheckBoxTreeItem班是專門設計用于支持樹形結構的選擇,未選擇的,和不確定的狀態。甲CheckBoxTreeItem實例可以是獨立或從屬。如果CheckBoxTreeItem實例是獨立的,則對其選擇狀態的任何更改都不會影響其父CheckBoxTreeItem實例和子實例。默認情況下,所有ChechBoxTreeItem實例都是相關的。
編譯并運行Example 13-5,然后選擇View Source Files項。您應該看到如圖13-7所示的輸出,其中選擇了所有子項。
圖13-7從屬CheckBoxTreeItem
要使CheckBoxTreeItem實例獨立,請使用以下setIndependent方法:rootItem.setIndependent(true);。
運行TreeViewSample應用程序時,其行為應如圖13-8所示進行更改。
圖13-8獨立CheckBoxTreeItem
?
相關的API文檔?
-
TreeView
-
TreeItem
-
TreeCell
-
Cell
-
TextField
-
CheckBoxTreeCell
-
CheckBoxTreeItem
總結
以上是生活随笔為你收集整理的JavaFX UI控件教程(十四)之Tree View的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: JavaFX UI控件教程(十三)之Ta
- 下一篇: 抖音人脸变换怎么弄