如何在Vaadin 24中展示含多种不同节点的树形结构?
在Vaadin 24+中构建多类型节点的树形结构(支持城市-街道-建筑-公寓-房间层级)
针对Vaadin 24+中TreeGrid仅支持单一类型节点的限制,我们可以通过统一节点接口+自定义渲染器的方式,优雅实现多领域类的树形层级展示,无需额外的中间包装类。
步骤1:定义统一的层级节点接口
让所有领域类(城市、街道、建筑、公寓、房间)实现同一个接口,规范子节点获取和节点类型标识:
// 统一节点接口 public interface HierarchicalNode { List<? extends HierarchicalNode> getChildren(); NodeType getNodeType(); } // 节点类型枚举 enum NodeType { CITY, STREET, BUILDING, APARTMENT, ROOM } // 领域类实现示例(以City为例) public class City implements HierarchicalNode { private String name; private List<Street> streets; @Override public List<? extends HierarchicalNode> getChildren() { return streets; } @Override public NodeType getNodeType() { return NodeType.CITY; } // 省略getter/setter }
其他领域类(Street、Building等)按相同方式实现接口即可。
步骤2:配置TreeGrid的层级数据源
基于HierarchicalDataProvider构建数据源,以统一接口为处理对象,无需关心具体领域类:
TreeGrid<HierarchicalNode> treeGrid = new TreeGrid<>(); // 假设cityList是顶层城市节点列表 List<City> cityList = getCityData(); HierarchicalDataProvider<HierarchicalNode, Void> dataProvider = new AbstractHierarchicalDataProvider<>() { @Override public int getChildCount(HierarchicalQuery<HierarchicalNode, Void> query) { return query.getParent() != null ? query.getParent().getChildren().size() : cityList.size(); } @Override public boolean hasChildren(HierarchicalNode item) { return !item.getChildren().isEmpty(); } @Override protected Stream<HierarchicalNode> fetchChildrenFromParent(HierarchicalNode parent, HierarchicalQuery<HierarchicalNode, Void> query) { if (parent == null) { return cityList.stream(); } return parent.getChildren().stream().skip(query.getOffset()).limit(query.getLimit()); } @Override public Object getId(HierarchicalNode item) { // 为不同类型节点生成唯一标识,避免重复 return switch (item.getNodeType()) { case CITY -> ((City) item).getName(); case STREET -> ((Street) item).getCity().getName() + "-" + ((Street) item).getName(); case BUILDING -> ((Building) item).getStreet().getCity().getName() + "-" + ((Building) item).getStreet().getName() + "-" + ((Building) item).getNumber(); case APARTMENT -> ((Apartment) item).getBuilding().getStreet().getCity().getName() + "-" + ((Apartment) item).getBuilding().getStreet().getName() + "-" + ((Apartment) item).getBuilding().getNumber() + "-" + ((Apartment) item).getNumber(); case ROOM -> ((Room) item).getApartment().getBuilding().getStreet().getCity().getName() + "-" + ((Room) item).getApartment().getBuilding().getStreet().getName() + "-" + ((Room) item).getApartment().getBuilding().getNumber() + "-" + ((Room) item).getApartment().getNumber() + "-" + ((Room) item).getNumber(); }; } }; treeGrid.setDataProvider(dataProvider);
步骤3:自定义渲染器,适配不同节点类型
通过ComponentRenderer为不同类型节点渲染差异化内容(如图标、文本格式):
// 主列:显示带图标的节点名称 treeGrid.addColumn(new ComponentRenderer<>(node -> { HorizontalLayout layout = new HorizontalLayout(); layout.setAlignItems(Alignment.CENTER); // 根据节点类型设置图标 Icon icon = switch (node.getNodeType()) { case CITY -> VaadinIcon.CITY.create(); case STREET -> VaadinIcon.MAP_MARKER.create(); case BUILDING -> VaadinIcon.BUILDING.create(); case APARTMENT -> VaadinIcon.HOME.create(); case ROOM -> VaadinIcon.BED.create(); }; // 根据节点类型设置显示文本 String label = switch (node.getNodeType()) { case CITY -> ((City) node).getName(); case STREET -> ((Street) node).getName(); case BUILDING -> "建筑 " + ((Building) node).getNumber(); case APARTMENT -> "公寓 " + ((Apartment) node).getNumber(); case ROOM -> "房间 " + ((Room) node).getNumber(); }; layout.add(icon, new Text(label)); return layout; })).setHeader("层级结构");
步骤4:可选:针对节点类型显示专属列
如果需要为特定类型节点展示专属信息,可以通过setVisibleProvider控制列的显示/隐藏:
// 为建筑节点添加"楼层数"列 Column<HierarchicalNode> floorColumn = treeGrid.addColumn(node -> { if (node.getNodeType() == NodeType.BUILDING) { return ((Building) node).getFloorCount(); } return ""; }).setHeader("楼层数"); // 仅当节点为建筑时显示该列 floorColumn.setVisibleProvider(node -> node.getNodeType() == NodeType.BUILDING);
这种方案直接基于领域类实现接口,避免了中间包装类的冗余,同时通过渲染器和可见性控制完美适配5层不同类型的树形结构。
内容的提问来源于stack exchange,提问作者tbeernot
相关产品推荐
相关产品推荐

