Отключение всех родительских узлов в JavaFX с одновременным сохранением всех дочерних узлов - PullRequest
0 голосов
/ 02 сентября 2018

Я пытаюсь создать представление дерева JavaFX, в котором есть элементы дерева флажков. Я требую, чтобы все узлы, у которых есть дети, были отключены. Но все дочерние элементы этих узлов должны быть кликабельными.

В принципе, если элемент дерева является листом, его следует включить и щелкнуть.

Это текущий код, который я использовал. Который отключает узлы с дочерними элементами, но проблема в том, что, поскольку он отключен, я не могу получить доступ к коду внутри родительского элемента.

public class App extends Application
{
public static void main( String[] args )
{
    launch(args);
}

private List<Dependant> myList = new ArrayList();

@SuppressWarnings("unchecked")
@Override
public void start(Stage primaryStage) {

    CheckBoxTreeItem<String> rootItem = new CheckBoxTreeItem("Root");
    final List<CheckBoxTreeItem<String>> treeItems = new ArrayList(6);
    for (int i = 0; i < 6; i++) {
        CheckBoxTreeItem<String> item = new CheckBoxTreeItem("L0"+i+"");
        item.setIndependent(true);
        treeItems.add(item);  
        myList.add(new Dependant("0"+i+"", "type1"));
    }
    rootItem.getChildren().addAll(treeItems);

    rootItem.setExpanded(true);
    rootItem.setIndependent(true);
    CheckBoxTreeItem<String> rootItem2 = new CheckBoxTreeItem("folder");
    final List<CheckBoxTreeItem<String>> treeItems2 = new ArrayList(6);
    for (int i = 0; i < 6; i++) {
        CheckBoxTreeItem<String> item = new CheckBoxTreeItem("L1"+i+"");
        item.setIndependent(true);
        treeItems2.add(item); 
        myList.add(new Dependant("0"+i+"", "type2"));
    }
    rootItem2.getChildren().addAll(treeItems2);
    rootItem2.setIndependent(true);
    rootItem.getChildren().set(2,rootItem2);

    TreeView tree = new TreeView(rootItem);

    tree.setCellFactory((Object item) -> {

        final CheckBoxTreeCell<String> cell = new CheckBoxTreeCell();

        cell.itemProperty().addListener((obs,s,s1)->{

            cell.disableProperty().unbind();
            if(s1!=null && !s1.isEmpty()){
                BooleanProperty prop = new SimpleBooleanProperty();
                prop.set((s1.equals("folder")));
                cell.disableProperty().bind(prop);
            }
        });
        return cell;
    });

    tree.setRoot(rootItem);

    StackPane root = new StackPane();
    root.getChildren().add(tree);
    primaryStage.setScene(new Scene(root, 300, 250));
    primaryStage.show();
  }

}

Это не окончательный код моего приложения, это просто пример кода, который я опробовал перед окончательной реализацией.

Как видно из кода, свойство disabled ячейки связано с тем, равно ли имя «папке», поскольку метод isLeaf() недоступен.

Если кому-то интересно, почему мне нужно отключить все родительские узлы, то это потому, что в финальной реализации мне требуется, чтобы во всем древовидном представлении был выбран только один элемент (так как щелчок по родительскому узлу также выберет всех его дочерних узлов!).

Спасибо.

1 Ответ

0 голосов
/ 04 сентября 2018

Хотя вопрос является типичной XY-проблемой (с просьбой решить проблему, которая была введена путем попытки решить реальное требование неверным образом), эта реальная проблема достаточно интересна, чтобы дать ей быстрая попытка:)

Реальное требование, насколько я понимаю:

  • есть дерево с элементами, которые позволяют выбор (через флажок)
  • разрешить выбор только одного элемента в любое время

Как всегда, основной подход заключается в добавлении такой логики в общую модель (вместо представления = ячейка). Для Treeview модель представляет собой treeItem, который сам по себе не может обрабатывать логику между элементами, поэтому ему необходим контроллер с более широкими знаниями.

К счастью, fx обеспечивает поддержку выбора один-во-многих: роль один играет Toggle, роль many играет ToggleGroup. Теперь все, что нам нужно сделать, это применить эту поддержку к нашему контексту:

  • реализовать пользовательский TreeItem, который является Toggle (пример ниже расширяет CheckBoxTreeItem)
  • в коде приложения используйте этот элемент и добавьте его в ToggleGroups при необходимости

Пример кода (остерегайтесь: неполная реализация и формально не проверена - просто чтобы указать направление!)

public class TreeSingleSelectedCheckboxWithToggle extends Application {

    /**
     * A custom CheckBoxTreeItem that implements Toggle.
     * 
     * To control which/how many items can be selected at any
     * time, add them to one or several ToggleGroups.
     * 
     * @author Jeanette Winzenburg, Berlin
     */
    public static class ToggleTreeItem<T> extends CheckBoxTreeItem<T>
        implements Toggle {


        public ToggleTreeItem() {
            super();
            init();
        }

        public ToggleTreeItem(T value) {
            super(value);
            init();
        }

        private void init() {
            // basically c&p from ToggleButton 
            selectedProperty().addListener(ov -> {
                final boolean selected = isSelected();
                final ToggleGroup tg = getToggleGroup();
                // Note: these changes need to be done before selectToggle/clearSelectedToggle since
                // those operations change properties and can execute user code, possibly modifying selected property again
                if (tg != null) {
                    if (selected) {
                        tg.selectToggle(ToggleTreeItem.this);
                    } else if (tg.getSelectedToggle() == ToggleTreeItem.this) {
                        // reflective access to package-private api  - use your own utility method
                        FXUtils.invokeMethod(ToggleGroup.class, tg, "clearSelectedToggle");
                       // tg.clearSelectedToggle();
                    }
                }

            });
        }
        /**
         * The {@link ToggleGroup} to which this {@code ToggleButton} belongs. A
         * {@code ToggleButton} can only be in one group at any one time. If the
         * group is changed, then the button is removed from the old group prior to
         * being added to the new group.
         */
        private ObjectProperty<ToggleGroup> toggleGroup;
        @Override
        public final void setToggleGroup(ToggleGroup value) {
            toggleGroupProperty().set(value);
        }

        @Override
        public final ToggleGroup getToggleGroup() {
            return toggleGroup == null ? null : toggleGroup.get();
        }

        @Override
        public final ObjectProperty<ToggleGroup> toggleGroupProperty() {
            if (toggleGroup == null) {
                toggleGroup = new ObjectPropertyBase<ToggleGroup>() {
                    private ToggleGroup old;
                    @Override protected void invalidated() {
                        final ToggleGroup tg = get();
                        if (tg != null && !tg.getToggles().contains(ToggleTreeItem.this)) {
                            if (old != null) {
                                old.getToggles().remove(ToggleTreeItem.this);
                            }
                            tg.getToggles().add(ToggleTreeItem.this);
                        } else if (tg == null) {
                            old.getToggles().remove(ToggleTreeItem.this);
                        }

                        old = tg;
                    }

                    @Override
                    public Object getBean() {
                        return ToggleTreeItem.this;
                    }

                    @Override
                    public String getName() {
                        return "toggleGroup";
                    }
                };
            }
            return toggleGroup;
        }
        @Override
        public Object getUserData() {
            // TODO Auto-generated method stub
            return null;
        }

        @Override
        public void setUserData(Object value) {
            // TODO Auto-generated method stub
        }

        @Override
        public ObservableMap<Object, Object> getProperties() {
            // TODO Auto-generated method stub
            return null;
        }

    }

    @SuppressWarnings("unchecked")
    @Override
    public void start(Stage primaryStage) {

        ToggleGroup toggleGroup = new ToggleGroup();
        ToggleTreeItem<String> rootItem = new ToggleTreeItem<>("Root");
        toggleGroup.getToggles().add(rootItem);
        final List<ToggleTreeItem<String>> treeItems = new ArrayList<>(6);
        for (int i = 0; i < 6; i++) {
            ToggleTreeItem<String> item = new ToggleTreeItem<>("L0" + i + "");
            item.setIndependent(true);
            treeItems.add(item);
            toggleGroup.getToggles().add(item);
            myList.add(new Dependant("0" + i + "", "type1"));
        }
        rootItem.getChildren().addAll(treeItems);

        rootItem.setExpanded(true);
        rootItem.setIndependent(true);
        ToggleTreeItem<String> rootItem2 = new ToggleTreeItem<>("folder");
        toggleGroup.getToggles().add(rootItem2);
        final List<ToggleTreeItem<String>> treeItems2 = new ArrayList<>(6);
        for (int i = 0; i < 6; i++) {
            ToggleTreeItem<String> item = new ToggleTreeItem<>("L1" + i + "");
            item.setIndependent(true);
            treeItems2.add(item);
            toggleGroup.getToggles().add(item);
            myList.add(new Dependant("0" + i + "", "type2"));
        }
        rootItem2.getChildren().addAll(treeItems2);
        rootItem2.setIndependent(true);
        rootItem.getChildren().set(2, rootItem2);

        TreeView tree = new TreeView<>(rootItem);

        tree.setCellFactory(CheckBoxTreeCell.forTreeView());

        tree.setRoot(rootItem);

        StackPane root = new StackPane();
        root.getChildren().add(tree);
        primaryStage.setScene(new Scene(root, 300, 250));
        primaryStage.show();
    }

    public static class Dependant {

        String one;
        String two;
        public Dependant(String one, String two) {
            this.one = one;
            this.two = two;
        }

    }

    private List<Dependant> myList = new ArrayList();


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

}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...