JavaFX:防止窗口小于包含的窗格

时间:2021-02-23 19:32:03

标签: javafx layout

我有一个 VBox 作为场景的根节点。里面的每个窗格都有自己的最小-最大高度,我让它们按照我想要的方式行事。我已将 VBox 设置为完全填充窗格。

我想要的另外两件事:

  1. 为了让 VBox 始终适合元素。当所有元素都达到最小值时停止收缩,当它们达到最大值时停止增长。
  2. 当 VBox 收缩时窗口停止收缩。

实际上,窗口不断缩小,而 VBox 在发生这种情况时消失在其中。

最好不要指定窗口的最小高度,因为这些组件的大小可能会发生变化。我希望自动计算最小高度。实现这一目标的最直接方法是什么?

这是我的 MWE:

package path.to.package;

import javafx.application.Application;
import javafx.geometry.Insets;
import javafx.scene.Scene;
import javafx.scene.control.Label;
import javafx.scene.layout.Border;
import javafx.scene.layout.BorderStroke;
import javafx.scene.layout.BorderStrokeStyle;
import javafx.scene.layout.BorderWidths;
import javafx.scene.layout.CornerRadii;
import javafx.scene.layout.Pane;
import javafx.scene.layout.Priority;
import javafx.scene.layout.VBox;
import javafx.scene.paint.Color;
import javafx.stage.Stage;

public class LayoutTestApp extends Application {

@Override
public void start(Stage primaryStage) throws Exception {

        Label labelA = new Label("Box A");
        Pane labelPaneA = new Pane(labelA);
        decoratePane(labelPaneA, 5, Color.TURQUOISE);
        labelPaneA.setMinHeight(20);
        labelPaneA.setMaxHeight(50);
        
        Label labelB = new Label("Box B");
        Pane labelPaneB = new Pane(labelB);
        decoratePane(labelPaneB, 5, Color.HOTPINK);
        labelPaneB.setMinHeight(30);
        labelPaneB.setMaxHeight(60);
       
        Label labelC = new Label("Box C");
        Pane labelPaneC = new Pane(labelC);
        decoratePane(labelPaneC, 5, Color.MAROON);
        labelPaneC.setMinHeight(20);
        labelPaneC.setMaxHeight(100);
        
        VBox root = new VBox(labelPaneA, labelPaneB, labelPaneC);
        decoratePane(root, 20, Color.BLACK);
        
        root.heightProperty().addListener((obs, ov, nv) -> {
            System.out.println("Root changed height to " + nv);
        });
        
        VBox.setVgrow(labelPaneA, Priority.ALWAYS);
        VBox.setVgrow(labelPaneB, Priority.ALWAYS);
        VBox.setVgrow(labelPaneC, Priority.ALWAYS);
        
        Scene scene = new Scene(root);
        
        //Edited after it was explained that this isn't necessary
        //root.prefHeightProperty().bind(scene.heightProperty());
        //root.prefWidthProperty().bind(scene.widthProperty());
        
        primaryStage.setScene(scene);
        primaryStage.show();
}

private static void decoratePane(Pane pane, double pw, Color color) {
    pane.setPadding(new Insets(pw, pw, pw, pw));
    pane.setBorder(new Border(new BorderStroke(color, 
            BorderStrokeStyle.SOLID, CornerRadii.EMPTY, BorderWidths.DEFAULT)));
}

}

1 个答案:

答案 0 :(得分:0)

这是一个真正的黑客;覆盖根节点的 layoutChildren() 以更新阶段的 minHeightmaxHeight。另请注意,VBox 不会将其最大高度限制为其子节点的最大高度,因此使用 VBox 您还需要覆盖 computeMaxHeight(...)

    VBox root = new VBox(labelPaneA, labelPaneB, labelPaneC) {
        @Override
        protected void layoutChildren() {
            super.layoutChildren();
            Scene scene = getScene();
            if (scene == null) return ;
            Stage stage = (Stage) scene.getWindow();
            if (stage == null) return ;
            double heightDelta = stage.getHeight() - scene.getHeight() ;
            stage.setMinHeight(computeMinHeight(-1) + heightDelta);
            stage.setMaxHeight(computeMaxHeight(-1) + heightDelta);
        }
        
        @Override
        protected double computeMaxHeight(double width) {
            return getChildren().stream()
                    .collect(Collectors.summingDouble(node -> node.maxHeight(width)));
        }
    };

不过肯定有更好的方法。