Issue
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.
in setCellFactory, what is the purpose of overriding call or updateItem method? which one to override in this case?
what is the different in item == null or boolean empty = true in updateItem? what case does the boolean empty = true handles?
I want my view to be like this
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.
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
Solution
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;
});
Answered By - kleopatra
Answer Checked By - Robin (JavaFixing Admin)