2017-02-03 11 views
1

У меня есть ситуация, когда время отображения TreeView, с двумя уровнями записей (родителей и детей), например, так:JavaFX: Добавление функциональности CTRL-клик для флажков вложенной CheckBoxTreeItem [с]

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

Все эти предметы являются стандартными CheckBoxTreeItem s. То, что я хочу сделать, состоит в том, чтобы щелкнуть CTRL на флажке parent item, выберите набор его дочерних элементов, согласно некоторым функциям. Например, здесь может потребоваться только первый дочерний элемент (т. Е. child item 1-1 и child item 2-1) в каждом дочернем списке, который будет выбран после нажатия CTRL на родительский флажок.

Возможно ли это? Насколько я вижу, нет никакого способа получить доступ к флажку и передать его, например. обработчик события 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); 
    } 
} 
+1

Вам нужно реализовать 'TreeCell' себя, вместо того чтобы использовать реализацию удобства' CheckBoxTreeCell' (который не дает вам доступ к «CheckBox», как вы отметили). Поскольку вы знаете, что ваши элементы дерева являются 'CheckBoxTreeItem', и вы работаете только с' String' в качестве своего типа, это не так сложно. –

+0

@James_D Вот чего я боялся. Я попытался сделать копию CheckBoxTreeCell для обращения к пакету javafx (желая изменить минимальную сумму кода, учитывая, что я в значительной степени не уверен, что он делает) и получил исключение IllegalAccessException о невозможности доступа к суперклассу DefaultTreeCell из моего CustomCBTC. Я думаю, что это больше проблема с моим пониманием Java в целом, хотя (логически я не понимаю, почему это не должно работать, но hey ho). В любом случае, попробовал еще раз, минимально скопировав код из CBTC в новый класс в моем собственном пакете, и, похоже, он работает, поэтому, спасибо! –

ответ

1

Вам нужна собственная реализация TreeCell. Это должно дать вам отправную точку, что позволяет реализовать дополнительные функции, вам нужно:

public class MyCheckBoxCell extends TreeCell<String> { 

    private final CheckBox checkBox = new CheckBox(); 

    private BooleanProperty currentSelectedBinding ; 

    // only need this if you are using the indeterminateProperty() of your 
    // CheckBoxTreeItems 
    private BooleanProperty currentIndeterminateBinding ; 

    public MyCheckBoxCell() { 

     // add extra event handling to the check box here... 

    } 

    @Override 
    protected void updateItem(String item, boolean empty) { 

     super.updateItem(item, empty); 

     if (empty) { 
      setText(null); 
      setGraphic(null); 
     } else { 
      setText(item); 
      setGraphic(checkBox); 
      if (currentSelectedBinding != null) { 
       checkBox.selectedProperty().unbindBidirectional(currentSelectedBinding); 
      } 
      if (currentIndeterminateBinding != null) { 
       checkBox.indeterminateProperty().unbindBidirectional(currentIndeterminateBinding); 
      } 
      if (getTreeItem() instanceof CheckBoxTreeItem) { 
       CheckBoxTreeItem cbti = (CheckBoxTreeItem<?>) getTreeItem(); 
       currentSelectedBinding = cbti.selectedProperty(); 
       checkBox.selectedProperty().bindBidirectional(currentSelectedBinding); 
       currentIndeterminateBinding = cbti.indeterminateProperty(); 
       checkBox.indeterminateProperty().bindBidirectional(currentIndeterminateBinding); 
      } 
     } 
    } 
} 
0

Как было предложено @James_D в комментариях по этому вопросу, одно решение заключается в создании пользовательской реализации TreeCell, который выставляет CheckBox.

Модифицированный 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); 
       } 
      } 
     } 
    } 
}