如何结合VirtualFlow创建自定义Skin?解决组件内容不显示问题
修复方案
问题根源
你的自定义Skin存在几个关键问题:
updateItemCount方法空实现,导致VirtualFlow无法感知数据总量的变化(包括初始化时的总量)- 手动在
VirtualCell的updateIndex中调用updateItem,违背了VirtualFlow的自动更新逻辑,反而会导致cell状态异常 - 未监听
items列表的变更,数据变化时无法同步更新VirtualFlow - VirtualFlow未正确绑定Skin的大小,导致没有可用空间渲染内容
具体修复步骤
- 实现
updateItemCount方法:在该方法中正确更新VirtualFlow的cell数量,并确保初始化时触发一次 - 移除
VirtualCell中自定义的updateIndex逻辑:VirtualFlow会自动管理cell的item更新,无需手动干预 - 监听
items列表的变化:在Skin中添加监听器,当items增删或替换时,触发updateItemCount更新数据总量 - 让VirtualFlow占满Skin的空间:将VirtualFlow的大小属性绑定到Skin的宽度和高度,确保渲染空间充足
修改后的完整代码
import javafx.application.Application; import javafx.collections.FXCollections; import javafx.collections.ListChangeListener; import javafx.collections.ObservableList; import javafx.scene.Scene; import javafx.scene.control.Control; import javafx.scene.control.IndexedCell; import javafx.scene.control.Skin; import javafx.scene.control.skin.CellSkinBase; import javafx.scene.control.skin.VirtualContainerBase; import javafx.scene.control.skin.VirtualFlow; import javafx.scene.layout.Priority; import javafx.scene.layout.VBox; import javafx.stage.Stage; import java.util.ArrayList; import java.util.List; public class NewMain extends Application { public static class MyControl<T> extends Control { private final ObservableList<T> items; public MyControl(ObservableList<T> items) { this.items = items; } public ObservableList<T> getItems() { return items; } @Override protected Skin<?> createDefaultSkin() { return new MySkin<>(this); } } public static class MySkin<T> extends VirtualContainerBase<MyControl<T>, VirtualCell<T>> { public MySkin(MyControl<T> control) { super(control); VirtualFlow<VirtualCell<T>> virtualFlow = getVirtualFlow(); // 绑定VirtualFlow大小到Skin,确保占满控件空间 virtualFlow.prefWidthProperty().bind(widthProperty()); virtualFlow.prefHeightProperty().bind(heightProperty()); // 设置Cell工厂 virtualFlow.setCellFactory(vf -> new VirtualCell<>()); // 监听items列表变化,自动更新数据总量 control.getItems().addListener((ListChangeListener<? super T>) c -> updateItemCount()); // 初始化时触发一次数据总量更新 updateItemCount(); getChildren().add(virtualFlow); } @Override protected int getItemCount() { return getSkinnable().getItems().size(); } @Override protected void updateItemCount() { super.updateItemCount(); getVirtualFlow().setCellCount(getItemCount()); } } public static class VirtualCell<T> extends IndexedCell<T> { private static int counter = 0; public VirtualCell() { System.out.println("Created:" + counter); counter++; } @Override protected void updateItem(T item, boolean empty) { super.updateItem(item, empty); if (empty || item == null) { setText(null); } else { setText(item.toString()); } } @Override protected Skin<?> createDefaultSkin() { return new CellSkinBase<>(this); } } @Override public void start(Stage primaryStage) { List<String> list = new ArrayList<>(); for (var i = 0; i < 100; i++) { list.add("Item " + i); } ObservableList<String> items = FXCollections.observableArrayList(list); var control = new MyControl<>(items); VBox.setVgrow(control, Priority.ALWAYS); var root = new VBox(control); Scene scene = new Scene(root, 600, 200); primaryStage.setScene(scene); primaryStage.show(); } public static void main(String[] args) { launch(args); } }
额外说明
- 遵循JavaFX控件标准做法:不在
Control构造器中直接设置Skin,而是重写createDefaultSkin方法 - VirtualFlow的大小绑定是渲染内容的关键,否则即使有数据也无法显示
- 监听items列表变化可保证数据动态更新时,控件能同步响应
内容的提问来源于stack exchange,提问作者SilverCube
相关产品推荐
相关产品推荐

