本节我们介绍在应用中构建树形结构、向树形结构中增加元素、处理事件、自定义元素。
TreeView类展示了一个水平结构的树。在每个树形结构里都有一个根节点。根节点包含多个子节点,子节点还可以包含多个子节点,最末尾的子节点叫做叶子。
下图是一个典型的树形结构:
当你要在你的javaFX应用中创建树形结构的时候,你需要首先创建一个TreeView对象,然后创建多个TreeItem对象,指定其中一个TreeItem对象为跟元素,然后把其他的TreeItem对象添加到跟元素上,然后把根元素添加到TreeView对象中。
我们可以通过TreeItem构造函数和setGraphic函数为树的节点指定图标。图标的推荐的大小是16*16。但实际上任何Node的对象都可以作为一个图标。
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); } @Override public void start(Stage primaryStage) { primaryStage.setTitle("Tree View Sample"); TreeItem<String> rootItem = new TreeItem<> ("Inbox", rootIcon); rootItem.setExpanded(true); for (int i = 1; i < 6; i++) { TreeItem<String> item = new TreeItem<> ("Message" + i); rootItem.getChildren().add(item); } TreeView<String> tree = new TreeView<> (rootItem); StackPane root = new StackPane(); root.getChildren().add(tree); primaryStage.setScene(new Scene(root, 300, 250)); primaryStage.show(); } }
setExpanded方法可以设置树的展开或关闭。运行如下图所示:
上面的例子中,树的节点都是字符串。节点可以是任何类型,包括UI控件和自定义组件。
TreeItem并不是扩展子Node类,因此我们不能为TreeItem使用可视化效果。
单元格工厂是用来为TreeView生成一个TreeCell实例,这个实例就代表一个TreeItem。这个功能是很有用的,比如我们要操作大量的数据,对这些数据进行更新或者再添加。
考虑一下这种场景,我们要对给定部门的员工数据进行修改,或者继续添加员工。
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("Jacob Smith", "Accounts Department"), new Employee("Isabella Johnson", "Accounts Department"), 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")); TreeItem<String> rootNode; public static void main(String[] args) { launch(args); } public TreeViewSample() { this.rootNode = new TreeItem<>("MyCompany Human Resources", rootIcon); } @Override public void start(Stage stage) { rootNode.setExpanded(true); for (Employee employee : employees) { TreeItem<String> empLeaf = new TreeItem<>(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<>( 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<>(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); } } }
上面的实例,展示出来员工和部门的关系,但是不能进行修改和添加。
import java.util.Arrays; import java.util.List; import javafx.application.Application; 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.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("Jacob Smith", "Accounts Department"), new Employee("Isabella Johnson", "Accounts Department"), 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")); TreeItem<String> rootNode; public static void main(String[] args) { Application.launch(args); } public TreeViewSample() { this.rootNode = new TreeItem<>("MyCompany Human Resources", rootIcon); } @Override public void start(Stage stage) { rootNode.setExpanded(true); for (Employee employee : employees) { TreeItem<String> empLeaf = new TreeItem<>(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<>( 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<>(rootNode); treeView.setEditable(true); treeView.setCellFactory((TreeView<String> p) -> new TextFieldTreeCellImpl()); box.getChildren().add(treeView); stage.setScene(scene); stage.show(); } private final class TextFieldTreeCellImpl extends TreeCell<String> { private TextField textField; public TextFieldTreeCellImpl() { } @Override public void startEdit() { super.startEdit(); if (textField == null) { createTextField(); } setText(null); setGraphic(textField); textField.selectAll(); } @Override public void cancelEdit() { super.cancelEdit(); setText((String) getItem()); setGraphic(getTreeItem().getGraphic()); } @Override public 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((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); } } }
import java.util.Arrays; import java.util.List; import javafx.application.Application; 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.beans.property.SimpleStringProperty; import javafx.event.ActionEvent; 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("Jacob Smith", "Accounts Department"), new Employee("Isabella Johnson", "Accounts Department"), 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")); TreeItem<String> rootNode = new TreeItem<>("MyCompany Human Resources", rootIcon); public static void main(String[] args) { Application.launch(args); } @Override public void start(Stage stage) { rootNode.setExpanded(true); for (Employee employee : employees) { TreeItem<String> empLeaf = new TreeItem<>(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<>(rootNode); treeView.setEditable(true); treeView.setCellFactory((TreeView<String> p) -> new TextFieldTreeCellImpl()); box.getChildren().add(treeView); stage.setScene(scene); stage.show(); } private final class TextFieldTreeCellImpl extends TreeCell<String> { private TextField textField; private final ContextMenu addMenu = new ContextMenu(); public TextFieldTreeCellImpl() { MenuItem addMenuItem = new MenuItem("Add Employee"); addMenu.getItems().add(addMenuItem); addMenuItem.setOnAction((ActionEvent t) -> { TreeItem newEmployee = new TreeItem<>("New Employee"); getTreeItem().getChildren().add(newEmployee); }); } @Override public void startEdit() { super.startEdit(); if (textField == null) { createTextField(); } setText(null); setGraphic(textField); textField.selectAll(); } @Override public void cancelEdit() { super.cancelEdit(); setText((String) getItem()); setGraphic(getTreeItem().getGraphic()); } @Override public 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((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); } } }
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); } @Override public void start(Stage primaryStage) { primaryStage.setTitle("Tree View Sample"); CheckBoxTreeItem<String> rootItem = new CheckBoxTreeItem<>("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<>("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(); } }