简体   繁体   English

Node.snapshot(null, null) 改变场景的大小

[英]Node.snapshot(null, null) changes size of Scene

I have Scene which is set to the Scene of my primaryStage that - amongst other nodes - contains a VBox with a TableView and some buttons.我有Scene设置为我的 primaryStage 的Scene - 在其他节点中 - 包含一个带有TableView和一些按钮的VBox When I take a snapshot on a row in the table using TableRow.snapshot(null, null) , the size of the Scene is changed.当我使用TableRow.snapshot(null, null)对表中的一行进行快照时, Scene的大小发生了变化。 The width is changed by about 10 pixels while the height is changed by about 40 - sometimes more than 600 (.) - pixels.宽度改变了大约 10 个像素,而高度改变了大约 40 - 有时超过 600 (.) - 像素。

This happens because Node.snapshot(null, null) invokes Scene.doCSSLayoutSyncForSnapshot(Node node) which seems to get the preferred size of all nodes in the size and recalculate the size using that.发生这种情况是因为Node.snapshot(null, null)调用Scene.doCSSLayoutSyncForSnapshot(Node node)似乎获得了大小中所有节点的首选大小并使用它重新计算大小。 This somehow returns the wrong values since my nodes only has preferred sizes specified and looks great before this method is invoked.这会以某种方式返回错误的值,因为我的节点仅指定了首选大小,并且在调用此方法之前看起来很棒。 Is there any way to prevent this?有什么办法可以防止这种情况发生吗?

The size change is a problem, but it is also a problem that the primary stage doesn't change size with the Scene that it contains.大小变化是个问题,但是初级阶段不随它所包含的Scene改变大小也是一个问题。

I have tried to create an MCVE reproducing the issue, but after a few days of trying to do this, I am still unable to reproduce the problem.我试图创建一个 MCVE 来重现该问题,但尝试了几天后,我仍然无法重现该问题。 The original program contains around 2000 lines of code that I don't want to post here.原始程序包含大约 2000 行代码,我不想在这里发布。

Why would Scene.doCSSLayoutSyncForSnapshot(Node node) compromise my layout when it is properly laid out in the first place?为什么Scene.doCSSLayoutSyncForSnapshot(Node node)一开始就正确布局时会损害我的布局? Can I somehow make sure that the layout is properly synced before this method is invoked to make sure that it doesn't change anything?在调用此方法以确保它不会更改任何内容之前,我能否以某种方式确保布局已正确同步?

Solved the issue. 解决了这个问题。 Had to copy my whole project and then remove parts of the code until the issue disappeared. 不得不复制我的整个项目,然后删除部分代码,直到问题消失。

Anyway. 无论如何。 I basically had three components in my application. 我的应用程序中基本上有三个组件。 A navigation component, a table compontent, and a status bar compontent. 导航组件,表组件和状态栏组件。 It looked like this: 它看起来像这样:

应用草图

The problem I had was that the width of the status bar and the width and height of the table component was increased whenever I took a snapshot of a row in the table. 我遇到的问题是,每当我拍摄表格中一行的快照时,状态栏的宽度以及表格组件的宽度和高度都会增加。

Apparently, this was due to the padding of the status bar compontent. 显然,这是由于状态栏组件的填充。 It had a right and left padding of 5 pixels, and once I removed the padding, the problem disappeared. 它有一个5像素的左右填充,一旦我删除了填充,问题就消失了。

The added 10 pixels in width made the BorderPane that contained all of this expand with the same amount of pixels, and since the table width was bound to the BorderPane width, it increased by the same amount. 增加的10个像素的宽度使包含所有这些扩展的BorderPane具有相同的像素数量,并且由于表格宽度绑定到BorderPane宽度,因此它增加了相同的量。 What I still don't understand though, is why the Stage that contains the BorderPane doesn't adjust to the new width. 但是我仍然不明白,为什么包含BorderPaneStage不会调整到新的宽度。

The component was properly padded before Scene.doCSSLayoutSyncForSnapshot(Node node) was invoked, so I don't understand why the extra width of ten pixels is added. 在调用Scene.doCSSLayoutSyncForSnapshot(Node node)之前,组件已正确填充,因此我不明白为什么要添加十个像素的额外宽度。

Anyhow: Removing the padding from the status bar component and instead padding the components inside the status bar fixed the issue. 无论如何:从状态栏组件中删除填充,而不是填充状态栏内的组件修复了问题。 If someone has a good explanation for this, I'm all ears. 如果有人对此有一个很好的解释,我会全力以赴。

Here's a MCVE where you can reproduce the issue by dragging a row in the table: 这是一个MCVE,您可以通过在表格中拖动一行来重现该问题:

import java.io.File;
import java.sql.SQLException;
import java.util.ArrayList;

import javafx.application.Application;
import javafx.beans.property.SimpleStringProperty;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.geometry.Insets;
import javafx.scene.Node;
import javafx.scene.Scene;
import javafx.scene.control.TableColumn;
import javafx.scene.control.TableRow;
import javafx.scene.control.TableView;
import javafx.scene.input.ClipboardContent;
import javafx.scene.input.Dragboard;
import javafx.scene.input.TransferMode;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.HBox;
import javafx.scene.layout.Region;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;

public class MCVE extends Application {

    private Stage primaryStage;
    private BorderPane rootLayout;
    private VBox detailsView;
    private StatusBar statusBar;

    public void start(Stage primaryStage) throws SQLException {

        this.primaryStage = primaryStage;
        this.primaryStage.setTitle("MCVE");

        initRootLayout();
        showStatusBar();
        showDetailsView();

        detailsView.prefWidthProperty().bind(rootLayout.widthProperty());
        detailsView.prefHeightProperty().bind(rootLayout.heightProperty());
    }

    @Override
    public void init() throws Exception {
        super.init();

    }

    public void initRootLayout() {
        rootLayout = new BorderPane();

        primaryStage.setWidth(1000);
        primaryStage.setHeight(600);

        Scene scene = new Scene(rootLayout);
        primaryStage.setScene(scene);
        primaryStage.show();
    }

    public void showStatusBar() {
        statusBar = new StatusBar();
        rootLayout.setBottom(statusBar);
    }

    public void showDetailsView() {
        detailsView = new VBox();
        rootLayout.setCenter(detailsView);

        setDetailsView(new Table(this));

        detailsView.prefHeightProperty().bind(primaryStage.heightProperty());
        detailsView.setMaxHeight(Region.USE_PREF_SIZE);
    }

    public static void main(String[] args) {
        launch(args);
    }

    public VBox getDetailsView() {
        return detailsView;
    }

    public void setDetailsView(Node content) {
        detailsView.getChildren().add(0, content);
    }

    public StatusBar getStatusBar() {
        return statusBar;
    }

    class StatusBar extends HBox {
        public StatusBar() {
            setPadding(new Insets(0, 5, 0, 5));

            HBox leftBox = new HBox(10);

            getChildren().addAll(leftBox);

            /**
             * CONTROL SIZES
             */
            setPrefHeight(28);
            setMinHeight(28);
            setMaxHeight(28);

            // Leftbox takes all the space not occupied by the helpbox.
            leftBox.prefWidthProperty().bind(widthProperty());

            setStyle("-fx-border-color: black;");
        }
    }

    class Table extends TableView<ObservableList<String>> {

        private ObservableList<ObservableList<String>> data;

        public Table(MCVE app) {

            prefWidthProperty().bind(app.getDetailsView().widthProperty());
            prefHeightProperty()
                    .bind(app.getDetailsView().heightProperty());

            widthProperty().addListener((obs, oldValue, newValue) -> {
                System.out.println("Table width: " + newValue);
            });

            setRowFactory(r -> {
                TableRow<ObservableList<String>> row = new TableRow<ObservableList<String>>();

                row.setOnDragDetected(e -> {
                    Dragboard db = row.startDragAndDrop(TransferMode.ANY);
                    db.setDragView(row.snapshot(null, null));

                    ArrayList<File> files = new ArrayList<File>();

                    // We create a clipboard and put all of the files that
                    // was selected into the clipboard.
                    ClipboardContent filesToCopyClipboard = new ClipboardContent();
                    filesToCopyClipboard.putFiles(files);

                    db.setContent(filesToCopyClipboard);
                });

                row.setOnDragDone(e -> {
                    e.consume();
                });

                return row;
            });

            ObservableList<String> columnNames = FXCollections.observableArrayList("Col1", "col2", "Col3", "Col4");

            data = FXCollections.observableArrayList();

            for (int i = 0; i < columnNames.size(); i++) {
                final int colIndex = i;

                TableColumn<ObservableList<String>, String> column = new TableColumn<ObservableList<String>, String>(
                        columnNames.get(i));

                column.setCellValueFactory((param) -> new SimpleStringProperty(param.getValue().get(colIndex).toString()));

                    getColumns().add(column);
            }

            // Adds all of the data from the rows the data list.
            for (int i = 0; i < 100; i++) {
                // Each column from the row is a String in the list.
                ObservableList<String> row = FXCollections.observableArrayList();

                row.add("Column 1");
                row.add("Column 2");
                row.add("Column 3");
                row.add("Column 4");

                // Adds the row to data.
                data.add(row);
            }

            // Adds all of the rows in data to the table.
            setItems(data);
        }
    }
}

This answer talks about it a little bit Set scene width and height这个答案稍微讲了一下设置场景的宽度和高度

but after diving into the source code I found that the resizing in snapshot is conditional on the scene never having a size set by one of its constructors.但是在深入研究源代码后,我发现快照中的调整大小取决于场景从未由其构造函数之一设置大小。

You can only set a scene's size in its constructors and never again.您只能在其构造函数中设置场景的大小,并且不能再设置。 That makes a little bit of sense, since its otherwise only used to size the window that contains it.这有点道理,因为它仅用于调整包含它的 window 的大小。 It is unfortunate that the snapshot code is not smart enough to use the window's dimensions when set by the user in addition to the scene's possible user settings.不幸的是,除了场景可能的用户设置之外,快照代码还不够聪明,无法在用户设置时使用窗口的尺寸。

None of this prevents resizing later, so if you depend on taking snapshots, you may want to make a best practice out of using the Scene constructors which take a width and height and sending them something above 0这些都不能阻止以后调整大小,因此如果您依赖于拍摄快照,您可能希望使用具有宽度和高度的场景构造函数并将它们发送到高于 0 的值来做出最佳实践

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM