自定义布局组件中的 JavaFX 布局子项

问题描述 投票:0回答:1

我尝试渲染很多节点,达到一定数量后,滚动变得非常慢。

因此,我使用虚拟化方法,类似于 TableView。由于我有复杂的布局要求(例如,有些节点可以与其他节点相邻,有些需要保持在自己的行上),TableView 不适合。

我已经创建了自己的组件,但是子组件的布局不起作用。滚动时,可以从BorderPane看到浅红色背景,但标签背景和标签文本根本不渲染。

有什么想法吗?

import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.control.Label;
import javafx.scene.control.ScrollPane;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.Pane;
import javafx.scene.layout.StackPane;
import javafx.stage.Stage;

public class VirtualNodesDemo extends Application {
    @Override
    public void start(Stage primaryStage) {
        int virtualNodes = 100;
        double nodeWidth = 100;
        double nodeHeight = 100;

        StackPane root = new StackPane();

        Pane content = new Pane() {
            @Override
            protected void layoutChildren() {
                System.out.println("content.layoutChildren() ...");

                getChildren().clear();

                ScrollPane scrollPane = (ScrollPane) getParent().getParent().getParent();

                double viewportHeight = scrollPane.getViewportBounds().getHeight();
                double scrollTop = scrollPane.getVvalue() * (getPrefHeight() - viewportHeight);

                double y = 0;

                for (int i = 0; i < virtualNodes; i++) {
                    if (y + nodeHeight > scrollTop && y < scrollTop + viewportHeight) {
                        Pane pane = createRealNode(i);
                        pane.resizeRelocate(10, y, nodeWidth, nodeHeight);
                        pane.layout();
                        getChildren().add(pane);
                    } else {
                        break;
                    }
                    y += nodeHeight + 10;
                }

                setPrefHeight(y);
            }

            private Pane createRealNode(int i) {
                BorderPane borderPane = new BorderPane();
                borderPane.setManaged(false);
                Label label = new Label("Node " + i);
                label.setStyle("-fx-background-color: #aaf;");
                borderPane.setCenter(label);
                borderPane.setStyle("-fx-background-color: #faa;");
                return borderPane;
            }
        };

        ScrollPane scrollPane = new ScrollPane(content);
        scrollPane.setFitToWidth(true);
        scrollPane.setVbarPolicy(ScrollPane.ScrollBarPolicy.ALWAYS);
        scrollPane.vvalueProperty().addListener(observable -> content.layout());
        root.getChildren().add(scrollPane);

        Scene scene = new Scene(root, 800, 600);
        primaryStage.setScene(scene);
        primaryStage.show();
    }
    
    public static void main(String[] args) {
        launch(args);
    }
}

当如下更改createRealNode时,标签和标签文本将按预期显示。但是,我需要能够使用 BorderPane(以及相关的,如 StackPane、GridPane),因为我需要渲染的节点非常大并且有很多子窗格/子窗格。

            private Pane createRealNode(int i) {
                Label label = new Label("Node " + i);
                label.setStyle("-fx-background-color: #aaf;");
                Pane p = new Pane();
                p.setManaged(false);
                p.getChildren().add(label);
                return p;
            }
java javafx
1个回答
0
投票

我同意所有提到的评论。 不要重新发明轮子!!

应对虚拟化,并不像你想象的那么简单。考虑改变对项目进行建模(数据对象)的方法,然后您可以使用 ListView 来使用单元工厂来渲染节点。

为了快速解决您遇到的问题,请在将窗格添加到子窗格后添加

pane.applyCss();
。这将解决您的问题,并且标签将显示在 BorderPane 中。但正如我所说,这是非常错误的做法。

Pane pane = createRealNode(i);
pane.resizeRelocate(10, y, nodeWidth, nodeHeight);
pane.layout();
getChildren().add(pane); 
pane.applyCss();

您可以像我上面提到的使用 ListView 方法想到类似下面的内容:(只是为了给您一些如何处理的想法)

enter image description here

import javafx.application.Application;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.scene.Scene;
import javafx.scene.control.Label;
import javafx.scene.control.ListCell;
import javafx.scene.control.ListView;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.StackPane;
import javafx.stage.Stage;

public class VirtualNodesDemo_Update extends Application {
    private final static String CSS = "data:text/css," +
            """
                .list-view .list-cell{
                    -fx-background-color: transparent !important;
                }
    """;
    double nodeWidth = 100;
    double nodeHeight = 100;

    @Override
    public void start(Stage primaryStage) {
        ObservableList<DataDetails> data = FXCollections.observableArrayList();
        for(int i=0;i<100;i++){
            data.add(new DataDetails("Node "+i, "#aaf", "#faa", nodeWidth, nodeHeight));
        }
        ListView<DataDetails> listView = new ListView<>();
        listView.setItems(data);
        listView.setMaxWidth(Double.MAX_VALUE);
        listView.setMaxHeight(Double.MAX_VALUE);
        listView.setCellFactory(param -> new CustomListCell());

        StackPane root = new StackPane();
        root.getChildren().add(listView);
        Scene scene = new Scene(root, 800, 600);
        scene.getStylesheets().add(CSS);
        primaryStage.setScene(scene);
        primaryStage.show();
    }
    
    public static void main(String[] args) {
        launch(args);
    }

    class CustomListCell extends ListCell<DataDetails>{

        BorderPane pane;
        Label label;

        public CustomListCell(){
            /* Build the structure as you like...*/
            pane = new BorderPane();
            label = new Label();
            pane.setCenter(label);
        }

        @Override
        protected void updateItem(DataDetails item, boolean empty) {
            super.updateItem(item, empty);
            if(item!=null && !empty){
                label.setText(item.getTitle());
                pane.setPrefSize(item.getWidth(), item.getHeight());
                pane.setMaxSize(item.getWidth(), item.getHeight());
                label.setStyle("-fx-background-color: "+item.getLabelBg());
                pane.setStyle("-fx-background-color: "+item.getDataBg());
                setGraphic(pane);
            }else{
                setGraphic(null);
            }
        }
    }
    class DataDetails{
        String title;
        String labelBg;
        String dataBg;
        double width;
        double height;

        /* Build your data object with all the details that you need to render.. */
        public DataDetails(String title, String labelBg, String dataBg, double width, double height) {
            this.title = title;
            this.labelBg = labelBg;
            this.dataBg = dataBg;
            this.width = width;
            this.height = height;
        }

        public String getTitle() {
            return title;
        }

        public String getLabelBg() {
            return labelBg;
        }

        public String getDataBg() {
            return dataBg;
        }

        public double getWidth() {
            return width;
        }

        public double getHeight() {
            return height;
        }
    }
}
最新问题
© www.soinside.com 2019 - 2025. All rights reserved.