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

如何结合VirtualFlow创建自定义Skin?解决组件内容不显示问题

修复方案

问题根源

你的自定义Skin存在几个关键问题:

  1. updateItemCount方法空实现,导致VirtualFlow无法感知数据总量的变化(包括初始化时的总量)
  2. 手动在VirtualCell的updateIndex中调用updateItem,违背了VirtualFlow的自动更新逻辑,反而会导致cell状态异常
  3. 未监听items列表的变更,数据变化时无法同步更新VirtualFlow
  4. 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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.13 20:53:15