响应式 UI-在 ScrollPane 中使用 TilePane 进行设计
Responsive UI-Design with a TilePane in a ScrollPane
我正在努力研究 Scroll- 和 Tilepanes atm,但我遇到了一个问题,如果不进行肮脏的黑客攻击,我将无法解决。
我有一个水平的 TilePane,它有 8 个图块,我将它设置为有 4 列,结果是 2 行有 4 个图块。
我把 TilePane 放在 HBox 中,因为如果我把它放在 StackPane 中,它会拉伸 tilepane 的大小,使我的 colum 设置无效。有点奇怪,设置 prefColumns/Rows 会重新计算 TilePane 的大小,而不是尝试设置 columns/rows 的实际数量,感觉更像是一个肮脏的 hack。
无论如何,将HBox直接放入ScrollPane中也不行,因为即使在第2行tile被切断后Scrollbars也不会出现。在 Stackpane 中再次设置该 HBox,然后我将其放入 ScrollPane 中就可以了。至少在我将 window 的宽度调整到如此小之前,tilepane 必须重新对齐瓷砖并出现第 3 行或更多行。
基本程序如下:
public class Main extends Application {
@Override
public void start(Stage stage) {
TilePane tilePane = new TilePane();
tilePane.setPadding(new Insets(5));
tilePane.setVgap(4);
tilePane.setHgap(4);
tilePane.setPrefColumns(4);
tilePane.setStyle("-fx-background-color: lightblue;");
HBox tiles[] = new HBox[8];
for (int i = 0; i < 8; i++) {
tiles[i] = new HBox(new Label("This is node #" + i));
tiles[i].setStyle("-fx-border-color: black;");
tiles[i].setPadding(new Insets(50));
tilePane.getChildren().add(tiles[i]);
}
HBox hbox = new HBox();
hbox.setAlignment(Pos.CENTER);
hbox.setStyle("-fx-background-color: blue;");
hbox.getChildren().add(tilePane);
StackPane stack = new StackPane();
stack.getChildren().add(hbox);
ScrollPane sp = new ScrollPane();
sp.setFitToHeight(true);
sp.setFitToWidth(true);
sp.setContent(stack);
stage.setScene(new Scene(sp, 800, 600));
stage.show();
}
public static void main(String[] args) {
launch();
}
}
我设法实现了我想要的行为,但它更像是一个非常肮脏的 hack。我向包含 TilePane 的 HBox 的高度和宽度添加了一个侦听器,并假设当高度发生变化时,因为宽度变得太小以至于删除了一列并添加了一个新行。为了能够做到这一点,我将 HBox 放在 VBox 中,这样它就不会随着 ScrollPane 的高度而增长。对于宽度我简单计算了一下如果有space显示另一个colum(最多4个),就去做。
更改如下:
public class Main extends Application {
private boolean notFirstPassHeight;
private boolean notFirstPassWidth;
@Override
public void start(Stage stage) {
TilePane tilePane = new TilePane();
tilePane.setPadding(new Insets(5));
tilePane.setVgap(4);
tilePane.setHgap(4);
tilePane.setPrefColumns(4);
tilePane.setStyle("-fx-background-color: lightblue;");
// I took the value from ScenicView
tilePane.prefTileWidthProperty().set(182);
HBox tiles[] = new HBox[8];
for (int i = 0; i < 8; i++) {
tiles[i] = new HBox(new Label("This is node #" + i));
tiles[i].setStyle("-fx-border-color: black;");
tiles[i].setPadding(new Insets(50));
tilePane.getChildren().add(tiles[i]);
}
ScrollPane sp = new ScrollPane();
sp.setFitToHeight(true);
sp.setFitToWidth(true);
StackPane stack = new StackPane();
VBox vbox = new VBox();
vbox.setStyle("-fx-background-color: red");
HBox hbox = new HBox();
hbox.setAlignment(Pos.CENTER);
hbox.setStyle("-fx-background-color: blue;");
hbox.getChildren().add(tilePane);
notFirstPassHeight = false;
notFirstPassWidth = false;
hbox.heightProperty().addListener((observable, oldValue, newValue) -> {
if (oldValue.doubleValue() < newValue.doubleValue() && notFirstPassHeight) {
tilePane.setPrefColumns(tilePane.getPrefColumns() - 1);
stack.requestLayout();
}
notFirstPassHeight = true;
});
hbox.widthProperty().addListener((observable, oldValue, newValue) -> {
if (oldValue.doubleValue() < newValue.doubleValue() && notFirstPassWidth && tilePane.getPrefColumns() <= 3
&& (newValue.doubleValue() / (tilePane.getPrefColumns() + 1)) > tilePane.getPrefTileWidth()) {
tilePane.setPrefColumns(tilePane.getPrefColumns() + 1);
stack.requestLayout();
}
notFirstPassWidth = true;
});
vbox.getChildren().add(hbox);
stack.getChildren().add(vbox);
sp.setContent(stack);
stage.setScene(new Scene(sp, 800, 600));
stage.show();
}
public static void main(String[] args) {
launch();
}
}
但是这种方法需要我
1.Know the Width of the Tiles in the Tilepane.
2.Consider Padding and Gap between tiles for my calculation to be accurate, which I dont do in my example.
如果你问我,这无论如何都不是一个好方法。对于这样一个基本的东西来说,过程太复杂了。必须有一种更好、更简单的方法来实现完全的可调整大小和 ScrollPane 中的 TilePanes 所需的行为。
在 TilePane
中设置 and/or 行的首选列数决定了该磁贴窗格的 prefWidth
和 prefHeight
值的计算。如果你想强制最大列数,你只需要使 maxWidth
等于计算的 prefWidth
:你可以用
tilePane.setMaxWidth(Region.USE_PREF_SIZE);
这意味着(只要 tile pane 被放置在管理布局的东西中),它永远不会比 pref 宽度更宽,这是计算以允许首选的列数。当然,它可能比那个小。 (请注意,如果您需要最小列数而不是最大列数,则可以对 setMinWidth
使用相同的技巧。)
滚动窗格的 fitToHeight
和 fitToWidth
属性将在为真时尝试将内容的高度(分别为宽度)调整为等于滚动窗格的高度(宽度)视口。这些操作将优先于内容的首选高度(宽度),但会尝试遵守最小高度(宽度)。
因此,同时调用 setFitToWidth(true)
和 setFitToHeight(true)
通常是错误的,因为这几乎总是会完全关闭滚动(只是强制内容与滚动窗格的视口大小相同).
所以在这里你想要让 tile pane 的最大宽度符合 pref 宽度,并将 tile pane 的宽度固定为滚动窗格视口的宽度(这样当你缩小window,它缩小了视口的宽度并创建了更多的列)。如果行数增长到足够大,这将添加一个垂直滚动条,并且仅当视口水平收缩到低于平铺窗格的最小宽度(计算为所有节点的首选宽度的最小值)时才添加水平滚动条包含)。
我认为您的原始代码的以下版本基本上可以满足您的需求:
import javafx.application.Application;
import javafx.geometry.Insets;
import javafx.geometry.Pos;
import javafx.scene.Scene;
import javafx.scene.control.Label;
import javafx.scene.control.ScrollPane;
import javafx.scene.layout.HBox;
import javafx.scene.layout.Region;
import javafx.scene.layout.TilePane;
import javafx.stage.Stage;
public class ScrollingTilePane extends Application {
@Override
public void start(Stage stage) {
TilePane tilePane = new TilePane();
tilePane.setPadding(new Insets(5));
tilePane.setVgap(4);
tilePane.setHgap(4);
tilePane.setPrefColumns(4);
tilePane.setStyle("-fx-background-color: lightblue;");
// dont grow more than the preferred number of columns:
tilePane.setMaxWidth(Region.USE_PREF_SIZE);
HBox tiles[] = new HBox[8];
for (int i = 0; i < 8; i++) {
tiles[i] = new HBox(new Label("This is node #" + i));
tiles[i].setStyle("-fx-border-color: black;");
tiles[i].setPadding(new Insets(50));
tilePane.getChildren().add(tiles[i]);
}
HBox hbox = new HBox();
hbox.setAlignment(Pos.CENTER);
hbox.setStyle("-fx-background-color: blue;");
hbox.getChildren().add(tilePane);
// StackPane stack = new StackPane();
// stack.getChildren().add(tilePane);
// stack.setStyle("-fx-background-color: blue;");
ScrollPane sp = new ScrollPane();
// sp.setFitToHeight(true);
sp.setFitToWidth(true);
sp.setContent(hbox);
stage.setScene(new Scene(sp, 800, 600));
stage.show();
}
public static void main(String[] args) {
launch();
}
}
请注意,如果您需要在滚动窗格内容之外更改 space 的背景颜色,您可以在外部样式中使用以下内容 sheet:
.scroll-pane .viewport {
-fx-background-color: red ;
}
我正在努力研究 Scroll- 和 Tilepanes atm,但我遇到了一个问题,如果不进行肮脏的黑客攻击,我将无法解决。
我有一个水平的 TilePane,它有 8 个图块,我将它设置为有 4 列,结果是 2 行有 4 个图块。 我把 TilePane 放在 HBox 中,因为如果我把它放在 StackPane 中,它会拉伸 tilepane 的大小,使我的 colum 设置无效。有点奇怪,设置 prefColumns/Rows 会重新计算 TilePane 的大小,而不是尝试设置 columns/rows 的实际数量,感觉更像是一个肮脏的 hack。
无论如何,将HBox直接放入ScrollPane中也不行,因为即使在第2行tile被切断后Scrollbars也不会出现。在 Stackpane 中再次设置该 HBox,然后我将其放入 ScrollPane 中就可以了。至少在我将 window 的宽度调整到如此小之前,tilepane 必须重新对齐瓷砖并出现第 3 行或更多行。
基本程序如下:
public class Main extends Application {
@Override
public void start(Stage stage) {
TilePane tilePane = new TilePane();
tilePane.setPadding(new Insets(5));
tilePane.setVgap(4);
tilePane.setHgap(4);
tilePane.setPrefColumns(4);
tilePane.setStyle("-fx-background-color: lightblue;");
HBox tiles[] = new HBox[8];
for (int i = 0; i < 8; i++) {
tiles[i] = new HBox(new Label("This is node #" + i));
tiles[i].setStyle("-fx-border-color: black;");
tiles[i].setPadding(new Insets(50));
tilePane.getChildren().add(tiles[i]);
}
HBox hbox = new HBox();
hbox.setAlignment(Pos.CENTER);
hbox.setStyle("-fx-background-color: blue;");
hbox.getChildren().add(tilePane);
StackPane stack = new StackPane();
stack.getChildren().add(hbox);
ScrollPane sp = new ScrollPane();
sp.setFitToHeight(true);
sp.setFitToWidth(true);
sp.setContent(stack);
stage.setScene(new Scene(sp, 800, 600));
stage.show();
}
public static void main(String[] args) {
launch();
}
}
我设法实现了我想要的行为,但它更像是一个非常肮脏的 hack。我向包含 TilePane 的 HBox 的高度和宽度添加了一个侦听器,并假设当高度发生变化时,因为宽度变得太小以至于删除了一列并添加了一个新行。为了能够做到这一点,我将 HBox 放在 VBox 中,这样它就不会随着 ScrollPane 的高度而增长。对于宽度我简单计算了一下如果有space显示另一个colum(最多4个),就去做。
更改如下:
public class Main extends Application {
private boolean notFirstPassHeight;
private boolean notFirstPassWidth;
@Override
public void start(Stage stage) {
TilePane tilePane = new TilePane();
tilePane.setPadding(new Insets(5));
tilePane.setVgap(4);
tilePane.setHgap(4);
tilePane.setPrefColumns(4);
tilePane.setStyle("-fx-background-color: lightblue;");
// I took the value from ScenicView
tilePane.prefTileWidthProperty().set(182);
HBox tiles[] = new HBox[8];
for (int i = 0; i < 8; i++) {
tiles[i] = new HBox(new Label("This is node #" + i));
tiles[i].setStyle("-fx-border-color: black;");
tiles[i].setPadding(new Insets(50));
tilePane.getChildren().add(tiles[i]);
}
ScrollPane sp = new ScrollPane();
sp.setFitToHeight(true);
sp.setFitToWidth(true);
StackPane stack = new StackPane();
VBox vbox = new VBox();
vbox.setStyle("-fx-background-color: red");
HBox hbox = new HBox();
hbox.setAlignment(Pos.CENTER);
hbox.setStyle("-fx-background-color: blue;");
hbox.getChildren().add(tilePane);
notFirstPassHeight = false;
notFirstPassWidth = false;
hbox.heightProperty().addListener((observable, oldValue, newValue) -> {
if (oldValue.doubleValue() < newValue.doubleValue() && notFirstPassHeight) {
tilePane.setPrefColumns(tilePane.getPrefColumns() - 1);
stack.requestLayout();
}
notFirstPassHeight = true;
});
hbox.widthProperty().addListener((observable, oldValue, newValue) -> {
if (oldValue.doubleValue() < newValue.doubleValue() && notFirstPassWidth && tilePane.getPrefColumns() <= 3
&& (newValue.doubleValue() / (tilePane.getPrefColumns() + 1)) > tilePane.getPrefTileWidth()) {
tilePane.setPrefColumns(tilePane.getPrefColumns() + 1);
stack.requestLayout();
}
notFirstPassWidth = true;
});
vbox.getChildren().add(hbox);
stack.getChildren().add(vbox);
sp.setContent(stack);
stage.setScene(new Scene(sp, 800, 600));
stage.show();
}
public static void main(String[] args) {
launch();
}
}
但是这种方法需要我
1.Know the Width of the Tiles in the Tilepane.
2.Consider Padding and Gap between tiles for my calculation to be accurate, which I dont do in my example.
如果你问我,这无论如何都不是一个好方法。对于这样一个基本的东西来说,过程太复杂了。必须有一种更好、更简单的方法来实现完全的可调整大小和 ScrollPane 中的 TilePanes 所需的行为。
在 TilePane
中设置 and/or 行的首选列数决定了该磁贴窗格的 prefWidth
和 prefHeight
值的计算。如果你想强制最大列数,你只需要使 maxWidth
等于计算的 prefWidth
:你可以用
tilePane.setMaxWidth(Region.USE_PREF_SIZE);
这意味着(只要 tile pane 被放置在管理布局的东西中),它永远不会比 pref 宽度更宽,这是计算以允许首选的列数。当然,它可能比那个小。 (请注意,如果您需要最小列数而不是最大列数,则可以对 setMinWidth
使用相同的技巧。)
滚动窗格的 fitToHeight
和 fitToWidth
属性将在为真时尝试将内容的高度(分别为宽度)调整为等于滚动窗格的高度(宽度)视口。这些操作将优先于内容的首选高度(宽度),但会尝试遵守最小高度(宽度)。
因此,同时调用 setFitToWidth(true)
和 setFitToHeight(true)
通常是错误的,因为这几乎总是会完全关闭滚动(只是强制内容与滚动窗格的视口大小相同).
所以在这里你想要让 tile pane 的最大宽度符合 pref 宽度,并将 tile pane 的宽度固定为滚动窗格视口的宽度(这样当你缩小window,它缩小了视口的宽度并创建了更多的列)。如果行数增长到足够大,这将添加一个垂直滚动条,并且仅当视口水平收缩到低于平铺窗格的最小宽度(计算为所有节点的首选宽度的最小值)时才添加水平滚动条包含)。
我认为您的原始代码的以下版本基本上可以满足您的需求:
import javafx.application.Application;
import javafx.geometry.Insets;
import javafx.geometry.Pos;
import javafx.scene.Scene;
import javafx.scene.control.Label;
import javafx.scene.control.ScrollPane;
import javafx.scene.layout.HBox;
import javafx.scene.layout.Region;
import javafx.scene.layout.TilePane;
import javafx.stage.Stage;
public class ScrollingTilePane extends Application {
@Override
public void start(Stage stage) {
TilePane tilePane = new TilePane();
tilePane.setPadding(new Insets(5));
tilePane.setVgap(4);
tilePane.setHgap(4);
tilePane.setPrefColumns(4);
tilePane.setStyle("-fx-background-color: lightblue;");
// dont grow more than the preferred number of columns:
tilePane.setMaxWidth(Region.USE_PREF_SIZE);
HBox tiles[] = new HBox[8];
for (int i = 0; i < 8; i++) {
tiles[i] = new HBox(new Label("This is node #" + i));
tiles[i].setStyle("-fx-border-color: black;");
tiles[i].setPadding(new Insets(50));
tilePane.getChildren().add(tiles[i]);
}
HBox hbox = new HBox();
hbox.setAlignment(Pos.CENTER);
hbox.setStyle("-fx-background-color: blue;");
hbox.getChildren().add(tilePane);
// StackPane stack = new StackPane();
// stack.getChildren().add(tilePane);
// stack.setStyle("-fx-background-color: blue;");
ScrollPane sp = new ScrollPane();
// sp.setFitToHeight(true);
sp.setFitToWidth(true);
sp.setContent(hbox);
stage.setScene(new Scene(sp, 800, 600));
stage.show();
}
public static void main(String[] args) {
launch();
}
}
请注意,如果您需要在滚动窗格内容之外更改 space 的背景颜色,您可以在外部样式中使用以下内容 sheet:
.scroll-pane .viewport {
-fx-background-color: red ;
}