2016-03-06

JavaFX: TreeView と ContextMenu (2)

JavaFX の TreeView にポップアップ・メニュー (ContextMenu) を表示するサンプルについての続きです。今回は、TreeItem に ContextMenu を関連付けてポップアップ・メニューを表示させるサンプルを紹介します。なお、今回紹介するサンプルでも、[1] のサイトで掲載されている素材を 32 x 32 に縮小して利用させていただきました。

考え方としては、TreeItem クラスを継承した新しいクラスを作成し、固有のポップアップメニュー (ContextMenu) を表示させるようにします。

実行例

今回のサンプルでは、内部クラスを追加するので少々ソースが長くなります。そのため、最初に実行例を示しておきます。

「りんご」が選択されました。

見た目は、前回のサンプルと代わり映えしませんが、最初のレベルである「野菜」「果物」を表示しているノードでは、マウスの右クリックでポップアップメニューを表示せず、「りんご」など末端のノードではポップアップメニューが表示されるように変更されています。

最初のレベル「野菜」「果物」を表示するノードには、TreeItem クラスを継承した TreeBranch というクラスを作成して使用しています。この継承したクラスでは、ポップアップメニュー (ContextMenu) を表示させる機能(メソッド)、getMenu() を追加して、ポップアップメニューに使用する ContextMenu のインスタンスを TreeCell へ返すようにしています。ただし、この TreeBranch では、そのメソッドを実装していますが null を返すだけです。

「りんご」など末端のノードには、TreeBranch クラスを継承した TreeLeaf というクラスを作成して使用していますが、ItemBranch クラスで追加されたメソッド、getMenu() をオーバーライドして固有のポップアップメニューを定義して、その ContextMenu のインスタンスを TreeCell 返すようにしています。

その上で TreeCell クラスを拡張した TreeCellImpl クラスで、TreeBranch クラスが返すgetMenu() メソッドが返す ContextMenu のインスタンスを表示する機能を紐付けています。これを、setCellFactory メソッドで、このサンプルの TreeView クラスの tree インスタンスが使用する TreeCell に設定しています。

以上の拡張は、参考サイト [2] を参考にしています。

ソース

動作環境は次の通りです。

  • OS: Fedora 23 (x86_64)
  • Java: jdk1.8.0_74-1.8.0_74-fcs.x86_64 (Oracle)
  • IDE: NetBeans IDE 8.1
リスト:Sample_TreeView2.java 
package sample_treeview2;

import javafx.application.Application;
import javafx.event.ActionEvent;
import javafx.scene.Node;
import javafx.scene.Scene;
import javafx.scene.control.ContextMenu;
import javafx.scene.control.MenuItem;
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.layout.StackPane;
import javafx.stage.Stage;
import static javafx.application.Application.launch;

public class Sample_TreeView2 extends Application {

    Image CAROT = new Image(getClass().getResource("carot.png").toString());
    Image EGGPLANT = new Image(getClass().getResource("eggplant.png").toString());
    Image TOMATO = new Image(getClass().getResource("tomato.png").toString());
    Image APPLE = new Image(getClass().getResource("apple.png").toString());
    Image GRAPE = new Image(getClass().getResource("grape.png").toString());
    Image BANANA = new Image(getClass().getResource("banana.png").toString());

    TreeView<String> tree;

    @Override
    public void start(Stage primaryStage) {

        TreeItem<String> top = new TreeItem<>("root");

        TreeBranch<String> nodeItemA = new TreeBranch<>("野菜");
        addNode(top, nodeItemA);
        nodeItemA.setExpanded(true);
        TreeBranch<String> nodeItemB = new TreeBranch<>("果物");
        addNode(top, nodeItemB);
        nodeItemB.setExpanded(true);

        TreeLeaf<String> nodeItemA1 = new TreeLeaf<>("にんじん", new ImageView(CAROT));
        addNode(nodeItemA, nodeItemA1);
        TreeLeaf<String> nodeItemA2 = new TreeLeaf<>("なす", new ImageView(EGGPLANT));
        addNode(nodeItemA, nodeItemA2);
        TreeLeaf<String> nodeItemA3 = new TreeLeaf<>("とまと", new ImageView(TOMATO));
        addNode(nodeItemA, nodeItemA3);

        TreeLeaf<String> nodeItemB1 = new TreeLeaf<>("りんご", new ImageView(APPLE));
        addNode(nodeItemB, nodeItemB1);
        TreeLeaf<String> nodeItemB2 = new TreeLeaf<>("ぶどう", new ImageView(GRAPE));
        addNode(nodeItemB, nodeItemB2);
        TreeLeaf<String> nodeItemB3 = new TreeLeaf<>("ばなな", new ImageView(BANANA));
        addNode(nodeItemB, nodeItemB3);

        tree = new TreeView<>(top);
        tree.setShowRoot(false);
        tree.setCellFactory((TreeView<String> p) -> new TreeCellImpl());

        StackPane root = new StackPane();
        root.getChildren().add(tree);

        Scene scene = new Scene(root, 200, 300);

        primaryStage.setTitle(getClass().getSimpleName());
        primaryStage.setScene(scene);
        primaryStage.show();
    }

    void addNode(TreeItem<String> parent, TreeItem<String> child) {
        parent.getChildren().add(child);
    }

    class TreeBranch<T> extends TreeItem<T> {

        public TreeBranch(final T value) {
            super(value);
        }

        public TreeBranch(final T value, final Node graphic) {
            super(value, graphic);
        }

        public ContextMenu getMenu() {
            return null;
        }
    }

    class TreeLeaf<T> extends TreeBranch<T> {

        public TreeLeaf(final T value) {
            super(value);
        }

        public TreeLeaf(final T value, final Node graphic) {
            super(value, graphic);
        }

        @Override
        public ContextMenu getMenu() {
            ContextMenu popup = new ContextMenu();
            MenuItem menuItem = new MenuItem("処理");
            menuItem.setOnAction((ActionEvent event) -> {
                TreeItem selItem = tree.getSelectionModel().getSelectedItem();
                System.out.println("「" + selItem.getValue() + "」が選択されました。");
            });
            popup.getItems().add(menuItem);

            return popup;
        }
    }

    public final class TreeCellImpl extends TreeCell<String> {

        @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());
                setContextMenu(((TreeBranch) getTreeItem()).getMenu());
            }
        }
    }

    public static void main(String[] args) {
        launch(args);
    }
}

参考サイト

  1. 野菜・果物アイコン 画像フリー素材集|無料素材倶楽部
  2. Is this possible that i can add context menu at TreeItem rather then TreeView in JavaFX - Stack Overflow

 

0 件のコメント: