Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

TreeView Java FX: hide a node

Is it possible to hide a node in a tree View (the same way it works for Root) without having to remove the node from the tree itself ? The aim is to be able to provide a filter and a "show/hide" option on certain item of the tree.

All help appreciated

Thanks

like image 890
BlackLabrador Avatar asked Feb 15 '26 15:02

BlackLabrador


1 Answers

I found this article by Christoph Keimel which I think should solve your problem.

Basically you can't do it without a hack. You can have a FilterableTreeItem which extends TreeItem in which you create a FilteredList.

public class FilterableTreeItem<T> extends TreeItem<T> {
private final ObservableList<TreeItem<T>> sourceList;

private final ObjectProperty<TreeItemPredicate<T>> predicate = new SimpleObjectProperty<>();

public FilterableTreeItem(T value) {
    super(value);
    sourceList = FXCollections.observableArrayList();
    FilteredList<TreeItem<T>> filteredList = new FilteredList<>(sourceList);

    filteredList.predicateProperty().bind(Bindings.createObjectBinding(() -> {
        return child -> {
            // Set the predicate of child items to force filtering
            if (child instanceof FilterableTreeItem) {
                FilterableTreeItem<T> filterableChild = (FilterableTreeItem<T>) child;
                filterableChild.setPredicate(predicate.get());
            }
            // If there is no predicate, keep this tree item
            if (predicate.get() == null) {
                return true;
            }
            // If there are children, keep this tree item
            if (!child.getChildren().isEmpty()) {
                return true;
            }
            // Otherwise ask the TreeItemPredicate
            return predicate.get().test(this, child.getValue());
        };
    }, predicate));

    setHiddenFieldChildren(filteredList);
}

/**
 * Set the hidden private field {@link TreeItem#children} through reflection and hook the hidden {@link ListChangeListener} in {@link TreeItem#childrenListener} to the list
 * 
 */
protected void setHiddenFieldChildren(ObservableList<TreeItem<T>> list) {
    Field children = ReflectionUtils.findField(getClass(), "children");
    children.setAccessible(true);
    ReflectionUtils.setField(children, this, list);

    Field childrenListener1 = ReflectionUtils.findField(getClass(), "childrenListener");
    childrenListener1.setAccessible(true);
    Object childrenListener = ReflectionUtils.getField(childrenListener1, this);

    list.addListener((ListChangeListener<? super TreeItem<T>>) childrenListener);
}

/**
 * Returns the list of children that is backing the filtered list.
 * 
 * @return underlying list of children
 */
public ObservableList<TreeItem<T>> getInternalChildren() {
    return sourceList;
}

public final ObjectProperty<TreeItemPredicate<T>> predicateProperty() {
    return predicate;
}

public final TreeItemPredicate<T> getPredicate() {
    return predicate.get();
}

public final void setPredicate(TreeItemPredicate<T> predicate) {
    this.predicate.set(predicate);
}

}

You will also need the TreeItemPredicate

@FunctionalInterface
public interface TreeItemPredicate<T> {
/**
 * Utility method to create a TreeItemPredicate from a given {@link Predicate}
 */
static <T> TreeItemPredicate<T> create(Predicate<T> predicate) {
    return (parent, value) -> predicate.test(value);
}

/**
 * Evaluates this predicate on the given argument.
 *
 * @param parent
 *            the parent tree item of the element or null if there is no
 *            parent
 * @param value
 *            the value to be tested
 * @return {@code true} if the input argument matches the
 *         predicate,otherwise {@code false}
 */
boolean test(TreeItem<T> parent, T value);
}

And you put it together in your Controller

            MyNode myRootNode = myService.getTreeRootNode();

        FilterableTreeItem<MyNode> rootItem = new FilterableTreeItem<>(myRootNode);
        rootItem.setExpanded(true);

        for (MyNode node : myRootNode.getChildren()) {
            FilterableTreeItem<MyNode> item = new FilterableTreeItem<>(node);
            rootItem.getInternalChildren().add(item);
        }


        tree.setRoot(rootItem);


        rootItem.predicateProperty().bind(Bindings.createObjectBinding(()
                -> TreeItemPredicate.<MyNode> create(myNode
                        -> myNode.isStyled() == hide.getValue())
                , hide));

hide is a SimpleBooleanProperty. My node has a boolean property styled.

private final BooleanProperty hide = new SimpleBooleanProperty();

The Code simply allows to hide the stlyed nodes or the not styled ones. I know it is stupid, but I just made a test

like image 120
fan Avatar answered Feb 17 '26 05:02

fan



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!