当前位置: 首页 > 知识库问答 >
问题:

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

段干麒
2023-03-14

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

我的域模型如下所示:

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

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

下面的控制器类包含一个树视图,我在其中设置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的一种弱点。为什么JavaFX给用户完整的TreeView,而你只需要检索一个TreeCell?

有没有一种方法可以这样做,或者我必须在同一个自定义TreeCell实现中实现两种不同的行为?

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个答案

贺劲
2023-03-14

为什么JavaFX给用户完整的TreeView,而你只需要检索一个TreeCell?

因为TreeItemTreeCell之间没有1-1的关系:TreeView只会创建少量的TreeCell(即使树有非常多的项)。TreeCells被重用来显示不同的TreeItems,例如,如果一些节点被展开/折叠,或者如果用户滚动。

这样做是为了性能。提供渲染的实际单元是相当大的对象:它们是用户界面组件,带有CSS样式等。显示的实际数据,即TreeItem相对较轻;它们通常只是String的简单包装。因此,这种机制允许使用具有大量数据的TreeView,而不会给性能带来巨大负担。为每个TreeItem创建一个TreeCell将不允许这样做。

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

这意味着您的设置根本无法工作。你基本上需要TreeCell

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

}

工厂:

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

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

然后呢

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

比如,,

public class TaskCellConfigurator implements CellConfigurator<Task> {
    public void configureCell(TreeCell<Component> cell, Task item, boolean empty) {
        // task-specific implementation...
    }
}

然而,我不确定额外的结构是否值得在这里付出努力。

 类似资料:
  • 我正在W3School页面中尝试一个简单的案例查询。 http://www.w3schools.com/sql/trysql.asp?filename=trysql_select_groupby_2 当我运行下面的查询时,我总是为所有的范围值“over”。如果价格低于500,则应显示“under”。

  • 我正在尝试更新数据库表。如果表单中的密码为空,我如何通过提供一个条件来防止表单中的密码(“memberpassword”,$pass)被sql代码更新?有可能吗?

  • 我必须在不同的环境中绑定具有不同值的参数,并且在这方面存在问题。 我在尝试这个: 但它迫使我在services.yaml和services_dev.yaml文件中定义所有服务,否则它不起作用。 我想有一个services.yaml共享的任何环境,并只重写自定义服务/绑定等,而不是有两个相同的文件与所有的服务列在他们改变一个绑定值。 真正的问题是,我必须创建两个具有相同接口的http客户端(real

  • 问题内容: 我在搜索“如何在for循环中提供多个条件?” 但是没有给出直接答案。 经过一番研究,我找到了正确的方法。条件不得以逗号(,)或分号(;)分隔。我们可以使用&&运算符将两个条件结合在一起。 for(初始化; condition1 && condition2;增量) 例: 希望这对新的Java开发人员有所帮助。 问题答案: 您还可以使用“或”运算符,

  • 我有一个静态页面,我想有条件地服务于特定的URL 使用spring boot,我可以将页面放置在静态或公共资源目录中,并将其提供给所有人,但如果我想通过功能标志限制或禁用对它们的访问,那么这并不合适 使用模板引擎,我可以将页面作为模板加载,并返回对视图的引用。然而,我的应用程序相当简单,当我不需要模板引擎时,我不想使用模板引擎 我希望能够使用控制器来确定是否提供页面。让控制器返回静态页面的最简单方

  • 假设我有一个方法: 但有时当我运行这个方法时,我不需要对任何东西进行同步。 什么是有条件同步的好模式?我能想到的唯一模式是回调,类似于这样: 有没有别的方法,不用回拨?