JavaFX Cell Factory(2)樹狀物件
JavaFX Cell Factory(1)ComboBox與ListView << 前情 JavaFX的樹狀物件 (Tree) 由Tree View、Tree Item與Tree Cell所組成,TreeView類別如同ComboBox與ListView類別一般,將各節點視為單元,稱為樹狀單元 (Tree Cell),藉由TreeView類別的setCellFactory()方法設定Cell Factory,將每一個樹狀單元變更為其他物件,以改變節點的形式。 樹狀單元的類別為javafx.scene.control.TreeCell,繼承自TreeCell的類別包括:
以下範例以繼承TreeCell類別之自定TreeCellCallback類別做為setCellFactory()方法的回呼函式。 在TreeCellCallback類別的建構函式中建立快顯選單,當在節點上按下滑鼠右鍵時,則顯示快顯選單以新增節點。此外,為控制快顯選單只能在非葉節點上執行,類別中覆寫TreeCell類別的updateItem()方法,分別以isLeaf()方法判斷節點是否為葉節點、getParent()方法判斷上一層是否無節點 (則為根節點),若為葉節點與根節點,則不執行快顯選單: private final class TreeCellCallback extends TreeCell<String> { TextField textfield; ContextMenu contextmenu = new ContextMenu(); // 建構函式 public TreeCellCallback() { MenuItem menuitem = new MenuItem("Add Tree Node"); menuitem.setGraphic(new ImageView(new Image( getClass().getResourceAsStream("images/node.gif")))); menuitem.setOnAction((ActionEvent e) -> { TreeItem treeitem = new TreeItem<>("New Tree Node...", new ImageView(imgFile)); getTreeItem().getChildren().add(treeitem); }); contextmenu.getItems().add(menuitem); } @Override public void updateItem(String item, boolean empty) { super.updateItem(item, empty); if (empty) { setText(null); setGraphic(null); } else { setText(getItem() == null ? "" : getItem()); setGraphic(getTreeItem().getGraphic()); if (!getTreeItem().isLeaf() && getTreeItem().getParent()!= null){ setContextMenu(contextmenu); } } } } ... 待完成自定類別之後,須以TreeView類別的setCellFactory()方法以下列方式設定Cell Factory的回呼函式: treeView.setCellFactory( new Callback<TreeView<String>, TreeCell<String>>(){ @Override public TreeCell<String> call(TreeView<String> value) { return new TreeCellCallback(); } } ); ... 【執行結果】 以下範例分別覆寫TreeCell類別的startEdit()、cancelEdit()與updateItem()方法以處理開始編輯、取消編輯與更新樹狀單元等。 以開始編輯為例,在覆寫startEdit()方法中,當以滑鼠點選節點時,則以setGraphic()方法將節點設定為文字欄位,以此編輯節點標題。此外,並以setOnKeyReleased()方法處理在節點文字欄位釋放鍵盤按鍵時之事件,若按下Enter鍵,代表完成輸入,則以commitEdit()方法完成編輯樹狀單元;若按下ESC鍵,代表取消輸入,則以cancelEdit()方法取消編輯樹狀單元: // 覆寫TreeCell類別的startEdit()方法 @Override public void startEdit() { super.startEdit(); if (textfield == null) { textfield = new TextField(getItem() == null ? "" : getItem()); textfield.setOnKeyReleased((KeyEvent e) -> { if (e.getCode() == KeyCode.ENTER){ // 完成編輯樹狀單元 commitEdit(textfield.getText()); } else if (e.getCode() == KeyCode.ESCAPE) { // 取消編輯樹狀單元 cancelEdit(); } }); } setText(null); // 將節點設定為文字欄位 setGraphic(textfield); textfield.selectAll(); } ... 以取消編輯為例,在覆寫cancelEdit()方法中,由於取消編輯並未改變樹狀單元的內容,因此分別以setText()與setGraphic()方法設定為原標題文字與圖像,以恢復節點為原來的內容: // 覆寫TreeCell類別的cancelEdit()方法 @Override public void cancelEdit() { super.cancelEdit(); // 設定為原標題文字 setText((String) getItem()); // 將節點設定為原圖像 setGraphic(getTreeItem().getGraphic()); } ... 須注意的是,由於需要編輯節點,因此須以TreeView類別的setEditable()方法設定Tree View為可編輯狀態,並以TreeView類別的setCellFactory()方法以下列方式設定Cell Factory的回呼函式: treeView.setEditable(true); treeView.setCellFactory( new Callback<TreeView<String>, TreeCell<String>>(){ @Override public TreeCell<String> call(TreeView<String> value) { return new TreeCellCallback(); } } ); ... 【執行結果】 藉由繼承TreeCell類別之自定類別做為setCellFactory()方法的回呼函式,可使用各種物件取代原樹狀節點,以便編輯節點內容。 此方式雖然可行,但必須覆寫TreeCell類別的startEdit()、cancelEdit()、commitEdit()與updateItem()方法,因此有一定難度。 此外,JavaFX提供以下類別,可分別以核取方塊、選項方塊、複合方塊與文字欄位等取代原樹狀單元,類別彼此之間所提供的方法十分類似,使用上亦很容易:
CheckBoxTreeCell類別類似於CheckBox類別,以建立核取方塊樣式的樹狀節點。以下範例以TreeItem類別建立樹狀節點,並以TreeView類別的setCellFactory()方法設定Cell Factory為CheckBoxTreeCell,並以其forTreeView()方法設定以核取方塊取代原樹狀節點,相較於之前的範例,範例更為精簡: // 建立TreeView物件 TreeView treeView = new TreeView(); // 設定樹狀結構的根節點 treeView.setRoot(treeRoot); // 設定複選模式 treeView.getSelectionModel().setSelectionMode(SelectionMode.MULTIPLE); // 設定是否顯示根節點 treeView.setShowRoot(true); // 設定是否可以編輯 treeView.setEditable(true); // 設定Cell Factory treeView.setCellFactory(CheckBoxTreeCell.<String>forTreeView()); ... 【執行結果】 ChoiceBoxTreeCell類別類似於ChoiceBox類別,以建立選項方塊樣式的樹狀節點。以下範例以TreeItem類別建立樹狀節點,並以TreeView類別的setCellFactory()方法設定Cell Factory為ChoiceBoxTreeCell,並以其forTreeView()方法設定以選項方塊取代原樹狀節點,當點選節點時,則以「下拉式選單」的方式列出選項項目,其中以ObservableList設定選項方塊的選項項目: // 建立TreeView物件 TreeView treeView = new TreeView(); // 設定樹狀結構的根節點 treeView.setRoot(treeRoot); // 設定複選模式 treeView.getSelectionModel().setSelectionMode(SelectionMode.MULTIPLE); // 設定是否顯示根節點 treeView.setShowRoot(true); // 設定是否可以編輯 treeView.setEditable(true); // 設定選項方塊的選項項目 ObservableList<String> items = FXCollections.observableArrayList( "Profile", "Course", "Publication", "Book", "Project"); // 設定Cell Factory treeView.setCellFactory(ChoiceBoxTreeCell.forTreeView(items)); ... 【執行結果】 ComboBoxTreeCell類別類似於ComboBox類別,以建立複合方塊樣式的樹狀節點。以下範例以TreeItem類別建立樹狀節點,並以TreeView類別的setCellFactory()方法設定Cell Factory為ComboBoxTreeCell,並以其forTreeView()方法設定以複合方塊取代原樹狀節點,當點選節點時,則以「下拉式選單」的方式列出選項項目,其中以ObservableList設定複合方塊的選項項目: // 建立TreeView物件 TreeView treeView = new TreeView(); // 設定樹狀結構的根節點 treeView.setRoot(treeRoot); // 設定複選模式 treeView.getSelectionModel().setSelectionMode(SelectionMode.MULTIPLE); // 設定是否顯示根節點 treeView.setShowRoot(true); // 設定是否可以編輯 treeView.setEditable(true); // 設定複合方塊的選項項目 ObservableList<String> items = FXCollections.observableArrayList( "Profile", "Course", "Publication", "Book", "Project"); // 設定Cell Factory treeView.setCellFactory(ComboBoxTreeCell.forTreeView(items)); ... 【執行結果】 TextFieldTreeCell類別類似於TextField類別,以建立文字欄位樣式的樹狀節點,當以滑鼠點選節點時,則將原節點轉換為文字欄位,以此編輯節點標題。以下範例以TreeItem類別建立樹狀節點,並以TreeView類別的setCellFactory()方法設定Cell Factory為TextFieldTreeCell,並以其forTreeView()方法設定以文字欄位取代原樹狀節點,當以滑鼠點選節點時,則將原節點轉換為文字欄位,以此編輯節點標題: // 建立TreeView物件 TreeView treeView = new TreeView(); // 設定樹狀結構的根節點 treeView.setRoot(treeRoot); // 設定複選模式 treeView.getSelectionModel().setSelectionMode(SelectionMode.MULTIPLE); // 設定是否顯示根節點 treeView.setShowRoot(true); // 設定是否可以編輯 treeView.setEditable(true); // 設定Cell Factory treeView.setCellFactory(TextFieldTreeCell.forTreeView()); ... 【執行結果】 【參考資料】 |