ScrollPane 内部有间隙的 GridPane 呈现错误
GridPane with gaps inside ScrollPane rendering wrong
我想要一个带有方形单元格的矩形网格,它可以缩放和拖动(就像地图一样)。所以我把它放在 ScrollPane 中,尽管它在某些尺寸下表现得相当奇怪。网格的大小会动态变化。
图片上有一个大小为 20x20 的网格。
- 并非所有间隙都显示在网格内。注意它应该是 20 个方格
但不是。 Vgap 和 Hgap 都是 1.
- 网格本身比内容大,这不好,因为缩放时我可能会拖到网格的边界之外。
- 当 GridPane 小于滚动窗格时,我想 将它置于 ScrollPane 的中心。 (
Gridpane alignment="CENTER"
显然什么也没做)
最小、完整且可验证的示例
FXML:
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.scene.control.ScrollPane?>
<?import javafx.scene.layout.GridPane?>
<ScrollPane hbarPolicy="NEVER" hvalue="0.5" maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" pannable="true" prefHeight="400.0" prefWidth="600.0" vbarPolicy="NEVER" vvalue="0.5" xmlns="http://javafx.com/javafx/9" xmlns:fx="http://javafx.com/fxml/1" fx:controller="bug.app.Bug">
<GridPane fx:id="gridPane" alignment="CENTER" hgap="1.0" style="-fx-background-color: red;" vgap="1.0">
</GridPane>
</ScrollPane>
控制器+主机:
package bug.app;
import javafx.application.Application;
import javafx.beans.binding.DoubleBinding;
import javafx.beans.property.DoubleProperty;
import javafx.beans.property.SimpleDoubleProperty;
import javafx.fxml.FXML;
import javafx.fxml.FXMLLoader;
import javafx.geometry.Insets;
import javafx.scene.Node;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.scene.layout.*;
import javafx.scene.paint.Color;
import javafx.stage.Stage;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
public class Bug extends Application {
private static final int HEIGHT = 20;
private static final int WIDTH = 20;
private static final double MIN_CELL_SIZE = 2;
@FXML private GridPane gridPane;
private DoubleProperty zoomRatio = new SimpleDoubleProperty(5);
@FXML
private void initialize() {
final DoubleBinding cellSizeBinding = zoomRatio.multiply(MIN_CELL_SIZE);
List<Node> cells = new ArrayList<>(HEIGHT * WIDTH);
for (int row = 0; row < HEIGHT; row++) {
for (int col = 0; col < WIDTH; col++) {
final Pane cell = new Pane();
cell.setBackground(new Background(new BackgroundFill(Color.YELLOW, CornerRadii.EMPTY, Insets.EMPTY)));
cell.setMinSize(MIN_CELL_SIZE, MIN_CELL_SIZE);
cell.setMaxWidth(Region.USE_PREF_SIZE);
cell.setMaxHeight(Region.USE_PREF_SIZE);
cell.prefWidthProperty().bind(cellSizeBinding);
cell.prefHeightProperty().bind(cellSizeBinding);
GridPane.setConstraints(cell, col, row, 1, 1);
cells.add(cell);
}
}
gridPane.getChildren().clear();
gridPane.getChildren().addAll(cells);
}
public static void main(String[] args) {
launch(args);
}
@Override
public void start(Stage primaryStage) throws IOException {
Parent root = FXMLLoader.load(
getClass().getResource("/view/bugView.fxml"));
final Scene scene = new Scene(root, 600, 400);
primaryStage.setScene(scene);
primaryStage.show();
}
}
NB: 我选择了一个 Pane 作为 Cell,因为我想对其进行子类化并最终用不同的颜色填充它(可能是形状?),将正方形分成 1-4部分。想象两个或四个三角形组成一个正方形。我还想优雅地调整它的大小(AFAIK 形状不可调整大小),也许以某种方式将内部形状的大小绑定到窗格的大小会产生魔力。我还没有那么远,但也请随时就此提出建议。
将网格窗格包裹在堆栈窗格中,这将使网格窗格居中
问题 1 和 2
我找到了解决问题 1. 和 2. 的解决方法,省略了 Vgap 和 Hgap,因为显然它有问题。
我没有使用 GridPane 的间隙 属性,而是在每个单元格上设置边距:
GridPane.setMargin(cell, new Insets(0.5));
然而,由于 GridPane 上的默认值 snapToPixel="true"
,这会将边距四舍五入为 1,从而导致 2px 的间隙。这很不方便,因为我希望正方形小到 2x2px,所以 2px 的差距太大了。我可能会在 GridPane 上将 snapToPixel
设置为 false
。需要更多测试。
问题 3
我尝试按照@Silas Gyeme 的建议将 GridPane 包装在 StackPane 中。
仅将其包装在 StackPane 中是不够的。我必须在 ScrollPane 上同时设置 fitToHeight="true"
和 fitToWidth="true"
。
还必须将单元格的 minSize 绑定到 cellSizeBinding,因为当我将 window 缩小时它正在调整正方形的大小。
cell.minWidthProperty().bind(cellSizeBinding);
cell.minHeightProperty().bind(cellSizeBinding);
编辑: 我尝试省略 StackPane,但我实际上并不需要它!只需设置 fitToHeight
和 fitToWidth
就足够了,GridPane 会很好地拉伸。
我想要一个带有方形单元格的矩形网格,它可以缩放和拖动(就像地图一样)。所以我把它放在 ScrollPane 中,尽管它在某些尺寸下表现得相当奇怪。网格的大小会动态变化。
图片上有一个大小为 20x20 的网格。
- 并非所有间隙都显示在网格内。注意它应该是 20 个方格 但不是。 Vgap 和 Hgap 都是 1.
- 网格本身比内容大,这不好,因为缩放时我可能会拖到网格的边界之外。
- 当 GridPane 小于滚动窗格时,我想 将它置于 ScrollPane 的中心。 (
Gridpane alignment="CENTER"
显然什么也没做)
最小、完整且可验证的示例
FXML:
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.scene.control.ScrollPane?>
<?import javafx.scene.layout.GridPane?>
<ScrollPane hbarPolicy="NEVER" hvalue="0.5" maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" pannable="true" prefHeight="400.0" prefWidth="600.0" vbarPolicy="NEVER" vvalue="0.5" xmlns="http://javafx.com/javafx/9" xmlns:fx="http://javafx.com/fxml/1" fx:controller="bug.app.Bug">
<GridPane fx:id="gridPane" alignment="CENTER" hgap="1.0" style="-fx-background-color: red;" vgap="1.0">
</GridPane>
</ScrollPane>
控制器+主机:
package bug.app;
import javafx.application.Application;
import javafx.beans.binding.DoubleBinding;
import javafx.beans.property.DoubleProperty;
import javafx.beans.property.SimpleDoubleProperty;
import javafx.fxml.FXML;
import javafx.fxml.FXMLLoader;
import javafx.geometry.Insets;
import javafx.scene.Node;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.scene.layout.*;
import javafx.scene.paint.Color;
import javafx.stage.Stage;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
public class Bug extends Application {
private static final int HEIGHT = 20;
private static final int WIDTH = 20;
private static final double MIN_CELL_SIZE = 2;
@FXML private GridPane gridPane;
private DoubleProperty zoomRatio = new SimpleDoubleProperty(5);
@FXML
private void initialize() {
final DoubleBinding cellSizeBinding = zoomRatio.multiply(MIN_CELL_SIZE);
List<Node> cells = new ArrayList<>(HEIGHT * WIDTH);
for (int row = 0; row < HEIGHT; row++) {
for (int col = 0; col < WIDTH; col++) {
final Pane cell = new Pane();
cell.setBackground(new Background(new BackgroundFill(Color.YELLOW, CornerRadii.EMPTY, Insets.EMPTY)));
cell.setMinSize(MIN_CELL_SIZE, MIN_CELL_SIZE);
cell.setMaxWidth(Region.USE_PREF_SIZE);
cell.setMaxHeight(Region.USE_PREF_SIZE);
cell.prefWidthProperty().bind(cellSizeBinding);
cell.prefHeightProperty().bind(cellSizeBinding);
GridPane.setConstraints(cell, col, row, 1, 1);
cells.add(cell);
}
}
gridPane.getChildren().clear();
gridPane.getChildren().addAll(cells);
}
public static void main(String[] args) {
launch(args);
}
@Override
public void start(Stage primaryStage) throws IOException {
Parent root = FXMLLoader.load(
getClass().getResource("/view/bugView.fxml"));
final Scene scene = new Scene(root, 600, 400);
primaryStage.setScene(scene);
primaryStage.show();
}
}
NB: 我选择了一个 Pane 作为 Cell,因为我想对其进行子类化并最终用不同的颜色填充它(可能是形状?),将正方形分成 1-4部分。想象两个或四个三角形组成一个正方形。我还想优雅地调整它的大小(AFAIK 形状不可调整大小),也许以某种方式将内部形状的大小绑定到窗格的大小会产生魔力。我还没有那么远,但也请随时就此提出建议。
将网格窗格包裹在堆栈窗格中,这将使网格窗格居中
问题 1 和 2
我找到了解决问题 1. 和 2. 的解决方法,省略了 Vgap 和 Hgap,因为显然它有问题。
我没有使用 GridPane 的间隙 属性,而是在每个单元格上设置边距:
GridPane.setMargin(cell, new Insets(0.5));
然而,由于 GridPane 上的默认值 snapToPixel="true"
,这会将边距四舍五入为 1,从而导致 2px 的间隙。这很不方便,因为我希望正方形小到 2x2px,所以 2px 的差距太大了。我可能会在 GridPane 上将 snapToPixel
设置为 false
。需要更多测试。
问题 3
我尝试按照@Silas Gyeme 的建议将 GridPane 包装在 StackPane 中。
仅将其包装在 StackPane 中是不够的。我必须在 ScrollPane 上同时设置 fitToHeight="true"
和 fitToWidth="true"
。
还必须将单元格的 minSize 绑定到 cellSizeBinding,因为当我将 window 缩小时它正在调整正方形的大小。
cell.minWidthProperty().bind(cellSizeBinding);
cell.minHeightProperty().bind(cellSizeBinding);
编辑: 我尝试省略 StackPane,但我实际上并不需要它!只需设置 fitToHeight
和 fitToWidth
就足够了,GridPane 会很好地拉伸。