我正在尝试学习如何使用javafx,并且我遇到了一些javafx代码的行为,这些行为对我来说并不合适。我希望你能解释为什么javafx代码产生这些行为以及如何解决它们。
为了便于说明,我写了一个小例子程序:
import javafx.application.Application;
import javafx.geometry.Orientation;
import javafx.geometry.Pos;
import javafx.scene.Scene;
import javafx.scene.control.SplitPane;
import javafx.scene.control.ToggleButton;
import javafx.scene.image.Image;
import javafx.scene.image.ImageView;
import javafx.scene.layout.*;
import javafx.stage.Stage;
public class Example extends Application {
@Override
public void start(Stage primaryStage) {
// Top section with image
ImageView imageView = new ImageView(new Image("http://wfarm2.dataknet.com/static/resources/icons/set114/b4f29385.png"));
imageView.setPreserveRatio(true);
StackPane stackPane = new StackPane(imageView);
imageView.fitWidthProperty().bind(stackPane.widthProperty());
imageView.fitHeightProperty().bind(stackPane.heightProperty());
stackPane.getStyleClass().add("stack-pane");
// Bottom Section with CSS toggle
ToggleButton toggle = new ToggleButton("Show CSS");
HBox hbox = new HBox(toggle);
hbox.setAlignment(Pos.CENTER);
hbox.getStyleClass().add("h-box");
// Combines the two sections
SplitPane root = new SplitPane(stackPane, hbox);
root.setOrientation(Orientation.VERTICAL);
root.setResizableWithParent(hbox, false);
Scene scene = new Scene(root);
toggle.setOnMouseClicked(e -> {
if (toggle.isSelected()) {
scene.getStylesheets().add("StyleSheet.css");
toggle.setText("Disable CSS");
} else {
scene.getStylesheets().remove(0);
toggle.setText("Show CSS");
}
});
primaryStage.setScene(scene);
primaryStage.show();
}
public static void main(String[] args) {
launch(args);
}
}
StyleSheet.css文件仅包含以下三行:
.stack-pane { -fx-background-color: aqua; }
.split-pane { -fx-background-color: crimson; }
.h-box { -fx-background-color: chartreuse; }
最初我认为这个程序会在顶部显示一个图像 - 就像堆栈窗格一样 - 在窗口/阶段调整大小时总会调整大小。不幸的是,这并不是发生的事情:当窗口/阶段收缩时,堆栈窗格和图像没有正确调整大小。事实上,即使堆叠窗格的位置似乎也是偏移的,并且与分割窗格没有正确对齐。
以下图片旨在作为视觉参考: 它看起来像:
答案 0 :(得分:1)
当窗口/舞台缩小时,图像不会缩小
是的,这是设计的。图像和ImageView具有固定的大小。如果您想动态调整ImageView的大小,那么有几个选项可以在这个答案中概述:
如何修复示例程序?
使用上面可调整大小的图片中概述的技术之一。
启动后窗口/阶段是不是意味着缩小?
取决于您编写代码的方式。您可以这样编写,使其不会缩小,或者您可以编写它,以便在舞台大小更改时适当调整布局和内容。如何实现这一点取决于您作为开发人员。 JavaFX为可调整大小的布局定义了许多方法,其中一些在下面讨论:
实现可调整大小布局的标准方法是使用适当的layout panes。
即使堆栈窗格的位置似乎偏移并且未与拆分窗格
正确对齐
我没有详细分析您的代码中的这个问题。我认为这可能是由几件事造成的:
无论如何,故事的道德是:不要将孩子的宽度/高度绑定到父容器的宽度/高度。有时您可以使用这种方法,有时它会产生奇怪的副作用,例如您在示例代码中观察到的。
相反,您可以覆盖父节点中的布局传递逻辑,并在那里执行适当的布局计算。下面的示例中提供了此方法的示例。不幸的是,Oracle(或几乎网上的任何其他地方)没有关于覆盖父节点的布局传递逻辑的技术的零官方文档。大多数情况下,您不需要编写自定义布局逻辑,而是可以使用标准布局窗格和控件来获取所需的布局(尽管不幸的是,在这种情况下无法使用CSS后台设置或自定义布局代码。
使用-fx-shape
缩放位图图像可能会导致某些像素化并且图像不是很平滑。在您的情况下,输入位图图像在512x512处非常大,因此平滑缩放对于大图像来说并不是真正的问题,因为如果您只有例如较小的32x32图像并尝试将其扩展到512x512
由于您有一个简单的黑白图像,您可以将图像转换为svg,从svg中提取路径并使用region属性-fx-shape
。这是默认JavaFX样式表定义形状的方式,例如检查复选框,以便它们可以平滑地跨大小进行缩放。请参阅JRE实施中modena.css
内的文件jfxrt.jar
,了解-fx-shape
的定义和用法示例。
这样的东西会为你的图像做到这一点(可以将CSS样式提取到CSS文件以便于维护):
Pane tile = new Pane();
tile.setStyle("" +
"-fx-background-color: forestgreen;" +
"-fx-shape: \"M2470 4886 c-83 -22 -144 -60 -220 -136 -41 -41 -91 -102 -111 -135\n" +
"-121 -200 -2027 -3510 -2046 -3554 -55 -125 -68 -272 -33 -377 56 -162 205\n" +
"-260 431 -284 127 -13 4011 -13 4138 0 226 24 375 122 431 284 35 105 22 252\n" +
"-33 377 -32 72 -1994 3472 -2064 3577 -135 202 -320 295 -493 248z m223 -1182\n" +
"c122 -43 210 -155 237 -301 12 -69 3 -156 -45 -428 -34 -196 -101 -636 -120\n" +
"-785 -8 -69 -18 -144 -21 -168 l-5 -42 -177 2 -177 3 -8 75 c-20 203 -66 500\n" +
"-158 1030 -38 218 -40 240 -29 305 15 94 47 161 107 221 58 58 126 94 197 104\n" +
"61 9 147 2 199 -16z m30 -1989 c64 -32 144 -111 175 -172 49 -96 49 -231 0\n" +
"-332 -29 -59 -102 -133 -165 -164 -275 -140 -595 91 -543 391 14 80 39 130 93\n" +
"189 86 94 160 124 293 120 76 -2 99 -7 147 -32z\";"
);
tile.setScaleY(-1);
tile.setPrefSize(64, 64);
如果调整窗格大小,我不知道如何使窗格仅显示比例大小。所以我怀疑这对你有多大帮助。
如果JavaFX直接支持SVG图像but it does not。
,这会更简单一些布局创建和调试设备
示例解决方案
不同阶段尺寸的样本输出:
这使用在关于调整JavaFX中图像大小的链接问题的答案中定义的ImageViewPane。
ResizableImageSample.java
import javafx.application.Application;
import javafx.geometry.*;
import javafx.scene.Scene;
import javafx.scene.control.*;
import javafx.scene.image.*;
import javafx.scene.layout.HBox;
import javafx.stage.Stage;
public class ResizableImageSample extends Application {
@Override
public void start(Stage stage) {
// Top section with image
ImageView imageView = new ImageView(new Image(
"http://wfarm2.dataknet.com/static/resources/icons/set114/b4f29385.png"
));
imageView.setPreserveRatio(true);
ImageViewPane imagePane = new ImageViewPane(imageView);
imagePane.getStyleClass().add("stack-pane");
// Bottom Section with CSS toggle
ToggleButton toggle = new ToggleButton("Show CSS");
HBox hbox = new HBox(toggle);
hbox.setAlignment(Pos.CENTER);
hbox.getStyleClass().add("h-box");
// Combines the two sections
SplitPane root = new SplitPane(imagePane, hbox);
root.setDividerPositions(0.8);
root.setOrientation(Orientation.VERTICAL);
SplitPane.setResizableWithParent(hbox, false);
Scene scene = new Scene(root);
toggle.setOnMouseClicked(e -> {
if (toggle.isSelected()) {
scene.getStylesheets().add(getClass().getResource("StyleSheet.css").toExternalForm());
toggle.setText("Disable CSS");
} else {
scene.getStylesheets().remove(0);
toggle.setText("Show CSS");
}
});
stage.setScene(scene);
stage.show();
}
public static void main(String[] args) {
launch(args);
}
}
ImageViewPane.java
/*
* Copyright (c) 2012, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*/
import javafx.beans.property.ObjectProperty;
import javafx.beans.property.SimpleObjectProperty;
import javafx.geometry.HPos;
import javafx.geometry.VPos;
import javafx.scene.image.ImageView;
import javafx.scene.layout.Region;
/**
*
* @author akouznet
*/
class ImageViewPane extends Region {
private ObjectProperty<ImageView> imageViewProperty = new SimpleObjectProperty<>();
public ObjectProperty<ImageView> imageViewProperty() {
return imageViewProperty;
}
public ImageView getImageView() {
return imageViewProperty.get();
}
public void setImageView(ImageView imageView) {
this.imageViewProperty.set(imageView);
}
public ImageViewPane() {
this(new ImageView());
}
@Override
protected void layoutChildren() {
ImageView imageView = imageViewProperty.get();
if (imageView != null) {
if (imageView.isPreserveRatio()) {
if (getHeight() > getWidth()) {
imageView.setFitWidth(getWidth());
imageView.setFitHeight(0);
} else {
imageView.setFitWidth(0);
imageView.setFitHeight(getHeight());
}
} else {
imageView.setFitWidth(getWidth());
imageView.setFitHeight(getHeight());
}
layoutInArea(imageView, 0, 0, getWidth(), getHeight(), 0, HPos.CENTER, VPos.CENTER);
}
super.layoutChildren();
}
public ImageViewPane(ImageView imageView) {
imageViewProperty.addListener((observable, oldIV, newIV) -> {
if (oldIV != null) {
getChildren().remove(oldIV);
}
if (newIV != null) {
getChildren().add(newIV);
}
});
this.imageViewProperty.set(imageView);
}
}