You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.06 23:06:23