在CellFactory中有条件地提供不同的TreeCell

时间:2015-06-03 09:57:03

标签: javafx treeview javafx-2 custom-cell

我有一个树视图,其中单元格必须根据TreeItem's value的实际实现显示不同的信息。

我的域名模型如下:

Domain model

我似乎很自然地将“如何在单元格中显示任务”或“如何在单元格中显示组”的行为分为两个不同的类。

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
    }    
}

以下控制器类包含TreeView,其中我设置了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();
                }
            }
        });
    }
}

但是现在我陷入困境,决定我必须返回哪种细胞。事实上,我只有参数相关的TreeView而不是相关的TreeItem

在我看来,这似乎是JavaFX的一种弱点。当您只需要检索一个TreeCell时,为什么JavaFX会向用户提供完整的TreeView?

有没有办法以这种方式做到这一点,还是我必须在同一个自定义TreeCell实现中实现2种不同的行为?

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...
            }
       }
}

1 个答案:

答案 0 :(得分:2)

  

为什么当您只需要检索一个TreeCell时,JavaFX会向用户提供完整的TreeView?

因为TreeItemTreeCell s之间没有{1-1}关系:TreeView只会创建少量TreeCell s(甚至如果树有很多项目)。 TreeCell被重复使用以显示不同的TreeItem,例如,如果某些节点被展开/折叠,或者用户滚动。

这样做是为了表现。提供渲染的实际单元格是非常大的对象:它们是UI组件并带有CSS样式等。显示的实际数据,即TreeItem相对较轻;通常它们只是String的简单包装。因此,这种机制允许TreeView具有大量数据,这些数据不会对性能造成巨大负担。为每个TreeCell创建TreeItem都不允许这样做。

因此,您在工厂提供的TreeCell必须能够处理TreeItem可能提供给它的任何TreeView。例如,当用户更改显示的项目时(通过展开/折叠或滚动),之前显示TreeCell的{​​{1}}实例可能会用于显示Task 。这是Group方法的目的;在重用updateItem(...)时调用它。

这意味着您的设置无法正常工作。您基本上需要在上一个代码示例中使用TreeCell实现。如果您愿意,您当然可以将配置分解为单独的类,例如:

TreeCell<Component>

厂:

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);
    }

}

(注意,您可以承担工厂只使用单个线程,因为一切都将在FX应用程序线程上发生。)

然后

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 ;
        }
    }
}

,例如,

public interface CellConfigurator<T extends Component> {
    public void configureCell(ComponentTreeCell cell, T item, boolean empty);
}

但是,我不确定额外的结构是否值得付出努力。