3

Basically, I wanted to know if I could create a tree and custom it on javaFX... I tried to do it, but couldn't do anything so far with this code...

public class Main{
    ......

 public Main() throws Exception{
    ......    

   // TreeView created
    TreeView tv = (TreeView) fxmlLoader.getNamespace().get("treeview");

    TreeItem<String> rootItem = new TreeItem<String>("liss");
    rootItem.setExpanded(true);
    tv.setRoot(rootItem);

    /*for (int i = 1; i < 6; i++) {
        TreeItem<String> item = new TreeItem<String> ("Message" + i);
        rootItem.getChildren().add(item);
    }
    TreeItem<String> item = new TreeItem<String> ("MessageWoot");
    rootItem.getChildren().add(item);
*/
    //tv.setEditable(true);

    tv.setCellFactory(new Callback<TreeView<String>, TreeCell<String>>() {
        @Override
        public TreeCell<String> call(TreeView<String> arg0) {
            // custom tree cell that defines a context menu for the root tree item
            return new MyTreeCell();
        }
    });

    stage.show();
}

//
private static class MyTreeCell extends TextFieldTreeCell<String> {
    private ContextMenu addMenu = new ContextMenu();
    public boolean clickedFirstTime = false;

    public MyTreeCell() {
        // instantiate the root context menu
        MenuItem addMenuItem = new MenuItem("Expand");
        addMenu.getItems().add(addMenuItem);
        addMenuItem.setOnAction(new EventHandler() {

            public void handle(Event t) {
                TreeItem n0 =
                        new TreeItem<String>("'program'");
                TreeItem n1 =
                        new TreeItem<String>("<identifier>");
                TreeItem n2 =
                        new TreeItem<String>("body");

                getTreeItem().getChildren().add(n0);
                getTreeItem().getChildren().add(n1);
                getTreeItem().getChildren().add(n2);

            }
        });
    }

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

        // if the item is not empty and is a root...
        //if (!empty && getTreeItem().getParent() == null && this.clickedFirstTime) {
        System.out.println("UPDATEITEM -> clickedFirstTime : "+this.clickedFirstTime);
        if (!this.clickedFirstTime) {
            System.out.println("WOOT");
            setContextMenu(addMenu);
            this.clickedFirstTime = true;
        }
    }

}

And I'm questioning myself if this is the right "technology" which will solve what I'm trying to do...

What's my objective in this?

Firstly, I'm looking to add or delete a treeItem. I must say that a certain treeItem may be added only once or any N times, like a restriction (for example: treeItem < 6 for a certain level scope and a certain path of the root of tree view).

Secondly, make some treeItem editable and others not editable! When it is Editable, you may pop up something for the user in order to insert some input for example!

Is it possible ?

I saw the tutorial from https://docs.oracle.com/javafx/2/ui_controls/tree-view.htm#BABJGGGF but I'm really confused with this tutorial ... I don't really understand the cell factory mechanism... The fact that he does apply to TreeView when i want only a certain TreeItem... Or how could I control that effect/behaviour ? I mean, I'm really really lost with TreeView. Probably, TreeView isn't what I'm looking for ...

P.S.: I know that I cannot apply any visual effect or add menus to a tree items and that i use a cell factory mechanism to overcome this obstacle. Just I don't understand the idea and how could I do it !

4
  • What is the actual question here? All you seem to be saying is "I'm confused", but there's no specific question: "Here's some code, I expect it to do xxx, but it does yyy, why?". The cell factory mechanism is explained in the Cell documentation. Commented Jan 27, 2016 at 19:18
  • My question is, how do i do all my objectives and if it is possible doing it with TreeView Commented Jan 27, 2016 at 19:25
  • Cells also have an editable property. So you can call setEditable(...) in your updateItem() method, passing in true or false depending on the value of item, to achieve your second requirement. I'm not sure I really understand the first requirement, but you can similarly modify the menu in updateItem according to the item, or maybe check the tree item or item in the menu event handler and modify the behavior accordingly. You might need to elaborate on the first requirement to get an answer. Commented Jan 28, 2016 at 2:18
  • Ok, the first requirement might not be well explained but I'm gonna try to help. Imagine that my treeview has only one treeitem ( which is in this case a treeview with one level as deepest path, ok?). Now i right click this treeitem and it shows me a contextmenu where i can add more treeitem under the first treeitem. Now we have a treeview with two levels as deepest path ! But, now it is what i wanted and couldn't find it yet on the web...If i right click again my treeitem on the first level, it should be impossible to add more treeitems or i have N times which the add button may appear again! Commented Jan 28, 2016 at 9:46

2 Answers 2

6

Sure this is the right "technology", if you want to use JavaFX. You should probably use a more complex type parameter for TreeItem however. You can use your a custom TreeCell to allow the desired user interaction.

This example allows adding children and removing nodes via context menu (unless the content is "nocontext") as well as editing the content (as long as the content is not "noedit"); on the root node the delete option is disabled:

    tv.setEditable(true);
    
    tv.setCellFactory(new Callback<TreeView<String>, TreeCell<String>>() {

        private final MyContextMenu contextMenu = new MyContextMenu();
        private final StringConverter converter = new DefaultStringConverter();
        
        @Override
        public TreeCell<String> call(TreeView<String> param) {
            return new CustomTreeCell(contextMenu, converter);
        }
    
    });
public class CustomTreeCell extends TextFieldTreeCell<String> {

    private final MyContextMenu contextMenu;

    public CustomTreeCell(MyContextMenu contextMenu, StringConverter<String> converter) {
        super(converter);
        if (contextMenu == null) {
            throw new NullPointerException();
        }
        this.contextMenu = contextMenu;
        this.setOnContextMenuRequested(evt -> {
            prepareContextMenu(getTreeItem());
            evt.consume();
        });
    }

    private void prepareContextMenu(TreeItem<String> item) {
        MenuItem delete = contextMenu.getDelete();
        boolean root = item.getParent() == null;
        if (!root) {
            delete.setOnAction(evt -> {
                item.getParent().getChildren().remove(item);
                contextMenu.freeActionListeners();
            });
        }
        delete.setDisable(root);
        contextMenu.getAdd().setOnAction(evt -> {
            item.getChildren().add(new TreeItem<>("new item"));
            contextMenu.freeActionListeners();
        });
    }

    @Override
    public void updateItem(String item, boolean empty) {
        super.updateItem(item, empty);
        if (!empty) {
            setContextMenu("nocontext".equals(item) ? null : contextMenu.getContextMenu());
            setEditable(!"noedit".equals(item));
        }
    }

}
public class MyContextMenu {
    private final ContextMenu contextMenu;
    private final MenuItem add;
    private final MenuItem delete;

    public MyContextMenu() {
        this.add = new MenuItem("add child");
        this.delete = new MenuItem("delete");
        this.contextMenu = new ContextMenu(add, delete);
    }

    public ContextMenu getContextMenu() {
        return contextMenu;
    }

    public MenuItem getAdd() {
        return add;
    }

    public MenuItem getDelete() {
        return delete;
    }

    /**
     * This method prevents memory leak by setting all actionListeners to null.
     */
    public void freeActionListeners() {
        this.add.setOnAction(null);
        this.delete.setOnAction(null);
    }
    
}

Of course more complex checks can be done in the updateItem and prepareContextMenu and different user interactions can be supported (TextFieldTreeCell may not be the appropriate superclass for you; You could use a "normal" TreeCell and show a different stage/dialog to edit the item when the user selects a MenuItem in the context menu).

Some clarification about cell factories

Cell factories are used to create the cells in a class that displays data (e.g. TableColumn, TreeView, ListView). When such a class needs to display content, it uses it's cell factory to create the Cells that it uses to display the data. The content displayed in such a cell may be changed (see updateItem method).

Example

(I'm not 100% sure this is exactly the way it's done, but it should be sufficiently close)

A TreeView is created to display a expanded root node with 2 non expanded children.

The TreeView determines that it needs to display 3 items for the root node and it's 2 children. The TreeView therefore uses it's cell factory to creates 3 cells and adds them to it's layout and assigns the displayed items.

Now the user expands the first child, which has 2 children of it's own. The TreeView determines that it needs 2 more cells to display the items. The new cells are added at the end of the layout for efficiency and the items of the cells are updated:

  • The cell that previously contained the last child is updated and now contains the first child of the first item.
  • The 2 newly added cells are updated to contain the second child of the first child and the second child of the root respecitvely.
Sign up to request clarification or add additional context in comments.

6 Comments

Holy cow !!!! A lot of information here ! Firstly, I would like to understand the use of setCellFactory function on tv variable. Why do we put a setCellFactory to my treeview ? Secondly, what's the behaviour of call function in setCellFactory ? Each time, it is called a new CustomTreeCell is created. But why ? Thirdly, is there a way to change the contextmenu for example when i have activated a certain action ? I have a lot of others questions, but I will wait for the answer of those questions. And thank you for answering me :)
@Damiii: I just added some info about the cell factories. The there may not be one cell for each item in the view. Also it may be good if you learn the factory pattern ( oodesign.com/factory-pattern.html ) unless you already know about it. Here the "client" is the TreeView and it uses the factory to construct the "parts" it needs to display the info.
Does it mean that my ContextMenu, will be the same in the entire treeView ??? Because if yes, then this isn't the technology I'm looking for. I need to add a different ContextMenu for probably every treeItem/Cell factory which will be on the treeview.
@Damiii In my case I reused it for efficiency, but you could e.g. use a class as parameter type of TreeItem that contains a contextMenu property and bind the TreeCell's context menu or you could create a new one in the updateItem method or modify the existing one in the onContextMenuRequested handler or the updateItem, ect., ect..
Could you make an example for me ? Because I'm really lost... And i will be very thankful to you.
|
1

So I decided to eliminate TreeView (because the documentation is so trash...) and, instead, I decided to implement a Webview !

Why ?

Like that, I could create an HTML document and use jstree (jquery plugin - https://www.jstree.com ) in there. It is a plugin which will create the treeview basically.

And the documentation is ten time better than treeview oracle documentation unfortunately.

Also, the possibility in creating/editing the tree with jstree is better. Which concludes me that it was the best solution that I could figure out for me.

Also, whoever who will read me, I did a bridge with the webview and my javafx application ! It is a connection between my HTML document and the java application (Read more here - https://blogs.oracle.com/javafx/entry/communicating_between_javascript_and_javafx).

Hope It will help more people.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.