nanakondor
nanakondor

Reputation: 745

Modifying cellfactory of TreeView

I would like to change the disclosure node of expanding/unexpanding in Tree View without using -fx-background-image of CSS .arrow, because eventhough the image is 9*9 pixel, it shows so bad. I want to use the setCellFactory, but I don't know how.

I have several questions :

  1. in setCellFactory, what is the purpose of overriding call or updateItem method? which one to override in this case?

  2. what is the different in item == null or boolean empty = true in updateItem? what case does the boolean empty = true handles?

  3. I want my view to be like this

enter image description here

since TreeView can only contain one type of Item and Group A label is not a Person, other than putting Group A as a name of a Person object I guess I can only display the tree using a loop and put the TreeItems under a Box according to which Group they belong to. However, I don't know how to make expandable Box (use VBox?Hbox?StackPane?) please give me a hint about this

package DummyGUI;
import javafx.application.Application;

import javafx.scene.*;
import javafx.scene.control.*;
import javafx.scene.layout.*;
import javafx.stage.*;
import javafx.event.ActionEvent;
import javafx.scene.image.Image;
import javafx.scene.image.ImageView;

public class App extends Application
{
private final Image male = new Image(getClass().getResourceAsStream("male.png"), 16, 16, true, true);
private final Node maleIcon = new ImageView(male);

private final Image female = new Image(getClass().getResourceAsStream("female.png"), 16, 16, true, true);
private final Node femaleIcon = new ImageView(female);

private final Image plus = new Image(getClass().getResourceAsStream("plus-button.png"), 16, 16, true, true);
private final Node plusIcon = new ImageView(plus);

private final Image minus = new Image(getClass().getResourceAsStream("minus-button.png"), 16, 16, true, true);
private final Node minusIcon = new ImageView(minus);

private TreeView<Person> tree;
public static void main(String[] args) 
{
    launch();
}
public void start(Stage topView)
{
    createGUI(topView);
}
private void createGUI(Stage topView)
{
    topView.setTitle("Dummy App");
    initTree();
    VBox vb = new VBox(tree);
    topView.setScene(new Scene(vb));
    topView.show();
}

private void initTree()
{
    Person person1 = new Person("Charles", 'M', '0');
    Person person2 = new Person("John", 'M', 'A');
    Person person3 = new Person("Pearl", 'M', 'A');
    TreeItem<Person> root = new TreeItem<>(person1);
    TreeItem<Person> child1 = new TreeItem<>(person2);
    TreeItem<Person> child2 = new TreeItem<>(person3);
    tree = new TreeView<>(root);
    root.setExpanded(true);
    root.getChildren().addAll(child1, child2);

    tree.setCellFactory(tv -> 
    {
        HBox hb = new HBox();

        TreeCell<Person> cell = new TreeCell<Person>() 
        {
            @Override
            public void updateItem(Person item, boolean empty) 
            {
                super.updateItem(item, empty);

                if(empty)
                {
                    setGraphic(null);
                    setText(null);
                }
                else
                {
                    Node icon = (item.getGender() == 'M' ? maleIcon : femaleIcon);
                    setGraphic(icon);
                    setText(item.toString());
                }
            }
        };
        cell.setDisclosureNode(plusIcon);
        return cell;
    });
}
}



 package DummyGUI;

 public class Person 
 {
String name;
char gender;
char group;

public Person(String name, char gender, char group)
{
    this.name = name;
    this.gender = gender;
    this.group = group;
}

public String getName()
{
    return name;
}

public char getGender()
{
    return gender;
}

public char getGroup()
{
    return group;
}

public String toString()
{
    return name;
}

}

I tried to change the arrow icon to a plus icon but it has bug because I don't understand how to override updateItem.

enter image description here enter image description here

I don't know why the gender icon for John and Charles does not show when the tree is expanded and I dont know how to add minus icon. Thank you

Upvotes: 1

Views: 1240

Answers (2)

nanakondor
nanakondor

Reputation: 745

if (getTreeItem() != null) 
                {
                    setDisclosureNode(getTreeItem().isExpanded() ? expanded : collapsed);

                    setOnMouseClicked(event -> {
                        setDisclosureNode(getTreeItem().isExpanded() ? expanded : collapsed);
                    });

                }

its ok, i think with setOnMouseClicked it becomes good x0 thanks!

Upvotes: 0

kleopatra
kleopatra

Reputation: 51524

Minimalistic answer:

The basic problem is the re-use of the same instance of the nodes in all cells - nodes must have exactly one parent. Using the same, silently removes it from its former parent (or throws an exception)

The adapted cell

tree.setCellFactory(tv -> {
    // per-cell icons 
    Node maleIcon = new Button("m");
    Node femaleIcon = new Button("f");
    Node expanded = new Label("-");
    Node collapsed = new Label("+");
    TreeCell<Person> cell = new TreeCell<Person>() {
        @Override
        public void updateItem(Person item, boolean empty) {
            super.updateItem(item, empty);
            // check if the cell is empty (no data) or if the data is null
            if (item == null || empty) {
                setGraphic(null);
                setText(null);
            } else {
                Node icon = (item.getGender() == 'M' ? maleIcon
                        : femaleIcon);
                setGraphic(icon);
                // never-ever override toString for application reasons
                // instead set the text from data properties
                setText(item.getName());

            }
            if (getTreeItem() != null) {
                // update disclosureNode depending on expanded state
                setDisclosureNode(getTreeItem().isExpanded() ? expanded : collapsed);
            }
        }
    };
    return cell;
});

Upvotes: 3

Related Questions