JavaFX:将CTRL单击功能添加到嵌套CheckBoxTreeItem [s]的复选框

时间:2017-02-03 12:33:19

标签: java checkbox javafx treeview

我的情况是显示TreeView,有两个级别的条目(父母和孩子),如下所示:

root (invisible)
|_ parent item 1
   |_ child item 1-1
   |_ child item 1-2
|_ parent item 2
   |_ child item 2-1

这些项目都是标准的CheckBoxTreeItem。根据某些功能,我想要做的是按住CTRL键点击parent item的复选框,选择一组孩子。例如,在这里我可能只想在CTRL单击父复选框时选择每个子列表中的第一个子项(即child item 1-1child item 2-1)。

这可能吗?据我所见,没有好办法可以访问复选框并提供例如一个onMouseClick事件处理程序,这是对我有意义的解决方案。

上面给出的示例树布局的代码:

TreeViewTest.java

import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.control.CheckBoxTreeItem;
import javafx.scene.control.TreeView;
import javafx.scene.control.cell.CheckBoxTreeCell;
import javafx.scene.layout.StackPane;
import javafx.stage.Stage;

public class TreeViewTest extends Application {

    @Override
    public void start(final Stage stage) {
        StackPane sceneRoot = new StackPane();

        // create the tree model
        CheckBoxTreeItem<String> parent1 = new CheckBoxTreeItem<>("parent 1");
        CheckBoxTreeItem<String> parent2 = new CheckBoxTreeItem<>("parent 2");
        CheckBoxTreeItem<String> child1_1 = new CheckBoxTreeItem<>("child 1-1");
        CheckBoxTreeItem<String> child1_2 = new CheckBoxTreeItem<>("child 1-2");
        CheckBoxTreeItem<String> child2_1 = new CheckBoxTreeItem<>("child 2-1");
        CheckBoxTreeItem<String> root = new CheckBoxTreeItem<>("root");

        // attach the nodes
        parent1.getChildren().addAll(child1_1, child1_2);
        parent2.getChildren().addAll(child2_1);
        root.getChildren().addAll(parent1, parent2);

        // display everything
        root.setExpanded(true);
        parent1.setExpanded(true);
        parent2.setExpanded(true);

        // create the treeView
        final TreeView<String> treeView = new TreeView<>();
        treeView.setShowRoot(false);
        treeView.setRoot(root);

        // set the cell factory
        treeView.setCellFactory(CheckBoxTreeCell.forTreeView());

        // display the tree
        sceneRoot.getChildren().addAll(treeView);
        Scene scene = new Scene(sceneRoot, 200, 200);
        stage.setScene(scene);
        stage.show();
    }

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

2 个答案:

答案 0 :(得分:1)

您需要-std=c++11的自定义实现。这应该为您提供一个起点,允许您实现所需的其他功能:

TreeCell

答案 1 :(得分:0)

正如@James_D在该问题的评论中所建议的,一种解决方案是创建一个公开CheckBox的自定义TreeCell实现。

修改后的TreeViewTest.java

import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.control.CheckBoxTreeItem;
import javafx.scene.control.TreeCell;
import javafx.scene.control.TreeView;
import javafx.scene.input.MouseButton;
import javafx.scene.layout.StackPane;
import javafx.stage.Stage;
import javafx.util.Callback;

public class TreeViewTest extends Application {

    @Override
    public void start(final Stage stage) {
        StackPane sceneRoot = new StackPane();

        // create the tree model
        CheckBoxTreeItem<String> parent1 = new CheckBoxTreeItem<>("parent 1");
        CheckBoxTreeItem<String> parent2 = new CheckBoxTreeItem<>("parent 2");
        CheckBoxTreeItem<String> child1_1 = new CheckBoxTreeItem<>("child 1-1");
        CheckBoxTreeItem<String> child1_2 = new CheckBoxTreeItem<>("child 1-2");
        CheckBoxTreeItem<String> child2_1 = new CheckBoxTreeItem<>("child 2-1");
        CheckBoxTreeItem<String> root = new CheckBoxTreeItem<>("root");

        // attach the nodes
        parent1.getChildren().addAll(child1_1, child1_2);
        parent2.getChildren().addAll(child2_1);
        root.getChildren().addAll(parent1, parent2);

        // display everything
        root.setExpanded(true);
        parent1.setExpanded(true);
        parent2.setExpanded(true);

        // create the treeView
        final TreeView<String> treeView = new TreeView<>();
        treeView.setShowRoot(false);
        treeView.setRoot(root);

        // set the cell factory UPDATED
        treeView.setCellFactory(new Callback<TreeView<String>,TreeCell<String>>() {

            @Override
            public TreeCell<String> call(TreeView<String> param) {
                TreeCell<String> cell = new MyTreeCell<>();

                ((MyTreeCell) cell).getCheckBox().setOnMouseClicked(e -> {
                    if (!cell.getTreeItem().isLeaf())
                        if (e.isControlDown() && e.getButton() == MouseButton.PRIMARY)
                            System.out.println("CTRL-clicked");
                });

                return cell;
            }
        });

        // display the tree
        sceneRoot.getChildren().addAll(treeView);
        Scene scene = new Scene(sceneRoot, 200, 200);
        stage.setScene(scene);
        stage.show();
    }

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

MyTreeCell.java(稍微调整从CheckBoxTreeCell复制的代码)

import javafx.beans.property.BooleanProperty;
import javafx.beans.property.ObjectProperty;
import javafx.beans.property.SimpleObjectProperty;
import javafx.beans.value.ObservableValue;
import javafx.scene.control.CheckBox;
import javafx.scene.control.CheckBoxTreeItem;
import javafx.scene.control.TreeCell;
import javafx.scene.control.TreeItem;
import javafx.util.Callback;
import javafx.util.StringConverter;

public class MyTreeCell<T> extends TreeCell<T> {
    private final CheckBox checkBox;
    private ObservableValue<Boolean> booleanProperty;
    private BooleanProperty indeterminateProperty;

    public MyTreeCell() {
        this.getStyleClass().add("check-box-tree-cell");
        this.checkBox = new CheckBox();
        this.checkBox.setAllowIndeterminate(false);

        // by default the graphic is null until the cell stops being empty
        setGraphic(null);
    }

    // --- checkbox
    public final CheckBox getCheckBox() { return checkBox; }

    // --- selected state callback property
    private ObjectProperty<Callback<TreeItem<T>, ObservableValue<Boolean>>>
            selectedStateCallback =
            new SimpleObjectProperty<>(
                    this, "selectedStateCallback");

    public final ObjectProperty<Callback<TreeItem<T>, ObservableValue<Boolean>>> selectedStateCallbackProperty() {
        return selectedStateCallback;
    }

    public final void setSelectedStateCallback(Callback<TreeItem<T>, ObservableValue<Boolean>> value) {
        selectedStateCallbackProperty().set(value);
    }

    public final Callback<TreeItem<T>, ObservableValue<Boolean>> getSelectedStateCallback() {
        return selectedStateCallbackProperty().get();
    }

    @Override public void updateItem(T item, boolean empty) {
        super.updateItem(item, empty);

        if (empty) {
            setText(null);
            setGraphic(null);
        } else {    
            TreeItem<T> treeItem = getTreeItem();

            // update the node content
            setText((treeItem == null ? "" : treeItem.getValue().toString()));
            checkBox.setGraphic(treeItem == null ? null : treeItem.getGraphic());
            setGraphic(checkBox);

            // uninstall bindings
            if (booleanProperty != null) {
                checkBox.selectedProperty().unbindBidirectional((BooleanProperty)booleanProperty);
            }
            if (indeterminateProperty != null) {
                checkBox.indeterminateProperty().unbindBidirectional(indeterminateProperty);
            }

            // install new bindings.
            // We special case things when the TreeItem is a CheckBoxTreeItem
            if (treeItem instanceof CheckBoxTreeItem) {
                CheckBoxTreeItem<T> cbti = (CheckBoxTreeItem<T>) treeItem;
                booleanProperty = cbti.selectedProperty();
                checkBox.selectedProperty().bindBidirectional((BooleanProperty)booleanProperty);

                indeterminateProperty = cbti.indeterminateProperty();
                checkBox.indeterminateProperty().bindBidirectional(indeterminateProperty);
            } else {
                Callback<TreeItem<T>, ObservableValue<Boolean>> callback = getSelectedStateCallback();
                if (callback == null) {
                    throw new NullPointerException(
                            "The CheckBoxTreeCell selectedStateCallbackProperty can not be null");
                }

                booleanProperty = callback.call(treeItem);
                if (booleanProperty != null) {
                    checkBox.selectedProperty().bindBidirectional((BooleanProperty)booleanProperty);
                }
            }
        }
    }
}