Provide various TreeCells conditionally in CellFactory
I have a tree where the cells are supposed to display different information according to the actual implementation of the TreeItem Value .
My domain model looks like this:
It seemed natural to me to separate the behavior of "how to display a task in a cell" or "how to display a group in a cell" in two different classes.
public abstract class ComponentTreeCell<T extends Component> extends TreeCell<T>
{
@Override
protected void updateItem(T item, boolean empty)
{
//Some common logic...
}
}
public class GroupTreeCell extends ComponentTreeCell<Group>
{
@Override
protected void updateItem(Group item, boolean empty)
{
super.updateItem(item, empty);
//Some group-specific-logic
}
}
public class TaskTreeCell extends ComponentTreeCell<Task>
{
@Override
protected void updateItem(Task item, boolean empty)
{
super.updateItem(item, empty);
//Some task-specific-logic
}
}
The following controller class contains a TreeView where I have set the CellFactory.
public class Controller implements Initializable
{
@FXML
private TreeView<Component> treeview;
@Override
public void initialize(URL url, ResourceBundle bundle)
{
treeview.setCellFactory(new Callback<TreeView<Component>, TreeCell<Component>>()
{
@Override
public TreeCell<Component> call(TreeView<Component> arg0)
{
if(/* stuck */ instanceof Group)
{
return new GroupTreeCell();
}
else if(/* stuck */ instanceof Task)
{
return new TaskTreeCell();
}
else
{
return new DefaultTreeCell();
}
}
});
}
}
But here I am stuck at the moment to decide which cell I should return. Indeed, I only have a parameter associated with the TreeView and not associated with a TreeItem !
It seems to me like a weakness of JavaFX. Why does JavaFX provide the user with a full TreeView when you only need to restore one TreeCell ??
Is there a way to do it this way or do I need to implement 2 different behaviors in the same custom TreeCell implementation?
public class ComponentTreeCell extends TreeCell<Component>
{
@Override
protected void updateItem(Component item, boolean empty)
{
//Some common logic...
if(item instanceof Group)
{
//Group-specific logic...
}
else if(item instanceof Task)
{
//Task-specific logic...
}
else
{
//Default logic...
}
}
}
source to share
Why does JavaFX provide the user with a full TreeView when you only need to restore one TreeCell ??
Since there is no 1-1 relationship between TreeItem
and TreeCell
s: TreeView
will only create a small number TreeCell
(even if the tree has a very large number of elements), are TreeCell
reused to display different TreeItem
s, for example if some nodes are expanded / reset or if the user scrolls.
. , , : CSS .. . , , .. TreeItem
, ; String
s. , TreeView
, . TreeCell
TreeItem
.
Because of this TreeCell
, which one you provide from the factory should handle whatever TreeItem
can be provided to it TreeView
. For example, when the user changes the items that are displayed (by expanding / collapsing or scrolling), the Group
instance TreeCell
that was previously displayed can be used for the display Task
. This is the purpose of the method updateItem(...)
; it gets called on reuse TreeCell
.
This means that your installation simply won't work. You basically need the implementation TreeCell<Component>
in your last code example. You can of course split the configuration into separate classes if you like, for example:
public class ComponentTreeCell extends TreeCell<Component>
{
@Override
protected void updateItem(Component item, boolean empty)
{
super.updateItem(item, empty);
CellConfiguratorFactory.getConfigurator(item).configure(this, item, empty);
}
}
Factory:
public class CellConfiguratorFactory {
private static CellConfigurator<Task> taskCellConfigurator ;
private static CellConfigurator<Group> groupCellConfigurator ;
private static CellConfigurator<Component> defaultCellConfigurator ;
private static CellConfigurator getConfigurator(Component item) {
if (item instanceof Task) {
if (taskCellConfigurator == null) {
taskCellConfigurator = new TaskCellConfigurator();
}
return taskCellConfigurator ;
} else if (item instanceof Group) {
if (groupCellConfigurator == null) {
groupCellConfigurator = new GroupCellConfigurator();
}
return groupCellConfigurator ;
} else {
if (defaultCellConfigurator == null) {
defaultCellConfigurator = new DefaultCellConfigurator();
}
return defaultCellConfigurator ;
}
}
}
(Note that you can afford to assume that the factory is only used from one thread, since everything will happen on the FX application thread.)
And then
public interface CellConfigurator<T extends Component> {
public void configureCell(ComponentTreeCell cell, T item, boolean empty);
}
with for example
public class TaskCellConfigurator implements CellConfigurator<Task> {
public void configureCell(TreeCell<Component> cell, Task item, boolean empty) {
// task-specific implementation...
}
}
However, I'm not sure if the additional structure is worth the effort here.
source to share