我有一个窗格,可以通过覆盖layoutChildren方法来自定义它的子节点。它简化了这样:
import javafx.scene.control.Button;
import javafx.scene.control.Label;
import javafx.scene.layout.Pane;
import javafx.scene.text.Font;
public class MyPane extends Pane {
private Label lblTitel;
private Button btnStart;
public MyPane(Runnable sceneChanger) {
this.lblTitel = new Label("Title");
lblTitel.setFont(Font.font(16));
this.btnStart = new Button("Change Scene");
btnStart.setOnAction(e -> sceneChanger.run());
getChildren().addAll(lblTitel, btnStart);
}
@Override
protected void layoutChildren() {
lblTitel.setLayoutX(getWidth() / 2 - lblTitel.getWidth() / 2);
lblTitel.setLayoutY(4);
btnStart.setLayoutX(getWidth() / 2 - btnStart.getWidth() / 2);
btnStart.setLayoutY(getHeight() - 4 - btnStart.getHeight());
super.layoutChildren();
}
}
将窗格显示为舞台场景的根节点可正常工作。但是当我更改场景然后再将其更改回来时,窗格大小不再适合舞台视口。
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.layout.Pane;
import javafx.stage.Stage;
public class App extends Application {
private Stage window;
private Scene firstScene;
private Scene secondScene;
public static void main(String[] args) {
launch(args);
}
@Override
public void start(Stage window) {
this.window = window;
this.firstScene = new Scene(new MyPane(() -> window.setScene(secondScene)));
this.secondScene = new Scene(new SecondPane());
window.setScene(firstScene);
window.setWidth(800);
window.setHeight(600);
window.show();
}
private class SecondPane extends Pane {
public SecondPane() {
Button btnBack = new Button("back");
btnBack.setOnAction(e -> window.setScene(firstScene));
getChildren().add(btnBack);
}
}
}
在更改回第一个场景后,窗格很远,您可以看到标题在右侧比在水平中心更多。一旦我改变窗口的大小,窗格大小再次完美地适合阶段视口,一切都很好。所以我在切换场景后立即向窗口添加了一个自定义的大小增加。
Platform.runLater(() -> window.setWidth(window.getWidth() + 1));
它适用于大多数情况,但它是一个荒谬的解决方案,并且当舞台是例如时,它不起作用。最大化。
在将场景设置到舞台后,是否有任何方法可以使自定义布局窗格适合舞台视口,这适用于所有情况?
答案 0 :(得分:0)
首先,请注意,您可以使用现有布局窗格轻松完成示例代码中提供的布局。像
这样的东西BorderPane myPane = new BorderPane();
myPane.setTop(lblTitel);
myPane.setBottom(btnStart);
BorderPane.setAlignment(lblTitel, Pos.CENTER);
BorderPane.setAlignment(btnStart, Pos.CENTER);
BorderPane.setMargin(lblTitle, new Insets(4, 0, 0, 0));
BorderPane.setMargin(btnStart, new Insets(0, 0, 4, 0));
应该为您提供所需的布局。
通常,您应该始终更喜欢内置布局到自定义布局。
如果您发现自己确实需要自定义窗格,那么除了简单地布置子节点之外,还有很多工作要做。您的窗格需要覆盖方法以确定其可调整大小的范围,这意味着覆盖方法computeMin/Pref/MaxWidth()
和computeMin/Pref/MaxHeight()
。您还应通过覆盖getContentBias()
来确定内容偏差。 layoutChildren()
方法应确定子节点的大小及其位置。它应该通过确定它们的首选尺寸并在可能的情况下使用它们来做到这一点,并使用最小 - 最大范围内的尺寸。您可以通过在子节点上调用prefWidth()
,prefHeight()
等来确定这些大小。
最后,为了确保自定义布局正确地考虑边框和填充,您应该考虑布局计算中的任何插入。实用程序方法snappedTop/Right/Bottom/LeftInsets()
为您提供来自边框或填充的插入内容的值。
这是一个适合我的例子:这假设两个子节点都有HORIZONTAL
内容偏差(这里有意义),所以我将-1
传递给计算宽度的方法,以及计算高度的方法的计算宽度。
import javafx.geometry.Orientation;
import javafx.scene.control.Button;
import javafx.scene.control.Label;
import javafx.scene.layout.Pane;
import javafx.scene.text.Font;
import javafx.scene.text.TextAlignment;
public class MyPane extends Pane {
private Label lblTitel;
private Button btnStart;
public MyPane(Runnable sceneChanger) {
// updated to test for long text, wrapping, etc.
this.lblTitel = new Label("This is a really long title that might end up having to span multiple lines of text");
this.lblTitel.setWrapText(true);
this.lblTitel.setTextAlignment(TextAlignment.CENTER);
lblTitel.setFont(Font.font(16));
this.btnStart = new Button("Change Scene");
btnStart.setOnAction(e -> sceneChanger.run());
getChildren().addAll(lblTitel, btnStart);
}
@Override
public Orientation getContentBias() {
return Orientation.HORIZONTAL ;
}
@Override
public double computePrefWidth(double height) {
return Math.max(lblTitel.prefWidth(height), btnStart.prefWidth(height));
}
@Override
public double computeMinWidth(double height) {
return Math.max(lblTitel.minWidth(height), btnStart.minWidth(height));
}
@Override
public double computeMaxWidth(double height) {
return Math.max(lblTitel.maxWidth(height), btnStart.maxWidth(height));
}
@Override
public double computePrefHeight(double width) {
return lblTitel.prefHeight(width) + btnStart.prefHeight(width) + 8 ;
}
@Override
public double computeMinHeight(double width) {
return lblTitel.minHeight(width) + btnStart.minHeight(width) + 8 ;
}
@Override
public double computeMaxHeight(double width) {
return lblTitel.maxHeight(width) + btnStart.maxHeight(width) + 8 ;
}
@Override
protected void layoutChildren() {
double usableWidth = getWidth() - snappedLeftInset() - snappedRightInset() ;
double usableHeight = getHeight() - snappedTopInset() - snappedBottomInset() ;
double lblTitleWidth = lblTitel.prefWidth(-1);
if (lblTitleWidth > usableWidth) {
lblTitleWidth = Math.max(lblTitel.minWidth(-1), usableWidth);
}
double lblTitleHeight = lblTitel.prefHeight(lblTitleWidth);
if (lblTitleHeight > usableHeight) {
lblTitleHeight = Math.max(lblTitel.prefHeight(lblTitleWidth), usableHeight);
}
double lblTitleX = snappedLeftInset() + usableWidth / 2 - lblTitleWidth / 2 ;
lblTitel.resizeRelocate(lblTitleX, 4 + snappedTopInset(), lblTitleWidth, lblTitleHeight);
double btnStartWidth = btnStart.prefWidth(-1);
if (btnStartWidth > usableWidth) {
btnStartWidth = Math.max(btnStart.minWidth(-1), usableWidth);
}
double btnStartHeight = btnStart.prefHeight(btnStartWidth);
if (btnStartHeight > usableHeight) {
btnStartHeight = Math.max(btnStart.minHeight(btnStartWidth), usableHeight);
}
double btnStartX = snappedLeftInset() + usableWidth / 2 - btnStartWidth / 2 ;
double btnStartY = snappedTopInset() + getHeight() - 4 - btnStartHeight ;
btnStart.resizeRelocate(btnStartX, btnStartY, btnStartWidth, btnStartHeight);
}
}