在 Javafx 中对齐两个滚动网格面板
Aligning Two Scrolling Gridpanes in Javafx
我有两个包含要对齐的网格的滚动窗格。它们的单元格宽度都应为 40,所以我不确定它们为什么不对齐。尽管它们一开始看起来是对齐的,但它们逐渐变得不对齐。
Here, they are aligned.
But as you can see, it gradually becomes unaligned.
顶部网格(带有数字)和底部网格都包含在单独的滚动窗格中。如果我不这样做而是将两个网格放在一个 VBox 中并将其放入一个滚动窗格中,那么一切都会对齐。但是,我需要它们位于单独的滚动窗格中,以便我可以向下滚动下部网格,同时保持数字就位,并且数字仍然与网格一起水平滚动(我双向绑定了两个滚动窗格的 hscrollvalues)。
private void layerDisplay() {
BeatNumbers bn = new BeatNumbers(this.finalBeat, this.overlay, this::togglePlay,
this.features::setBeat);
VBox layerGrid = new VBox();
for (String layerName : this.layers.keySet()) {
LayerGrid lg = new LayerGrid(this.layers.get(layerName), layerName, this.finalBeat,
this.features::addUnit, this.features::deleteUnit, this.features::playUnit,
this.features::stopAllUnits, this.features::refresh);
layerGrid.getChildren().add(lg);
}
StackPane pane = new StackPane();
pane.getChildren().add(layerGrid);
pane.getChildren().add(this.overlay);
this.gridScrollPane.setContent(pane);
this.numberPane.setContent(bn);
VBox box = new VBox();
box.getChildren().addAll(this.numberPane, this.gridScrollPane);
this.setCenter(box);
}
你知道如何保持 BeatNumbers 和 layerGrid 对齐吗?当我向下滚动时,BeatNumbers 必须固定在屏幕顶部,但当我水平滚动时,它必须与网格一起水平移动。
谢谢!
编辑: 这是一个最小的可重现示例:
这是运行示例的代码
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;
public class MinimumReproducibleExample extends Application {
public static void main(String[] args) {
launch(args);
}
@Override
public void start(Stage stage) {
VBox notAlignedButScrolls = new NotAlignedButScrolls();
Scene scene = new Scene(notAlignedButScrolls, 400, 300);
stage.setTitle("Not Aligned But Scrolls");
stage.setScene(scene);
stage.show();
VBox alignedButDoesNotScroll = new AlignedButDoesNotScroll();
Scene scene2 = new Scene(alignedButDoesNotScroll, 400, 300);
Stage stage2 = new Stage();
stage2.setTitle("Aligned But Does Not Scroll");
stage2.setScene(scene2);
stage2.show();
}
}
这是 NotAlignedButScrolls 的 class,它创建了一个示例,其中两个网格按我希望的方式滚动,但数字逐渐与网格不对齐.
import javafx.scene.control.ScrollPane;
import javafx.scene.control.ScrollPane.ScrollBarPolicy;
import javafx.scene.input.ScrollEvent;
import javafx.scene.layout.HBox;
import javafx.scene.layout.Priority;
import javafx.scene.layout.VBox;
public class NotAlignedButScrolls extends VBox {
public NotAlignedButScrolls() {
super();
// Setting up the scroll panes
// This is the top scroll pane
ScrollPane numberPane = new ScrollPane();
numberPane.addEventFilter(ScrollEvent.SCROLL, event -> {
if (event.getDeltaY() != 0) {
event.consume();
}
});
numberPane.setHbarPolicy(ScrollBarPolicy.NEVER);
numberPane.setVbarPolicy(ScrollBarPolicy.NEVER);
numberPane.setMaxHeight(15);
numberPane.setMinHeight(15);
// This is the lower scroll pane
ScrollPane gridScrollPane = new ScrollPane();
gridScrollPane.setHbarPolicy(ScrollBarPolicy.AS_NEEDED);
gridScrollPane.setVbarPolicy(ScrollBarPolicy.AS_NEEDED);
// Bind their horizontal scroll values together
gridScrollPane.hvalueProperty().bindBidirectional(numberPane.hvalueProperty());
// Set up the content for each scroll pane
HBox beatNumbers = this.beatNumbers();
VBox layerGrid = new VBox();
for (int i = 0; i < 28; i++) {
layerGrid.getChildren().add(this.gridPane());
}
gridScrollPane.setContent(layerGrid);
numberPane.setContent(beatNumbers);
gridScrollPane.prefHeightProperty().bind(this.heightProperty());
// Put everything together
VBox box = new VBox();
box.getChildren().addAll(numberPane, gridScrollPane);
this.getChildren().add(box);
}
private HBox beatNumbers() {
HBox beatNumbers = new HBox();
HBox.setHgrow(beatNumbers, Priority.ALWAYS);
for (int i = 0; i < 150; i++) {
VBox unit = new VBox();
unit.setMinWidth(40);
unit.setStyle("-fx-border-width: 0 1 0 0;-fx-border-color: green;");
Label measureNumber = new Label(String.valueOf(i + 1));
measureNumber.setStyle("-fx-text-fill: black");
unit.getChildren().add(measureNumber);
beatNumbers.getChildren().add(unit);
}
return beatNumbers;
}
private HBox gridPane() {
HBox gridPane = new HBox();
gridPane.setMinHeight(40);
HBox.setHgrow(gridPane, Priority.ALWAYS);
gridPane.setStyle("-fx-background-color: gray;-fx-border-style: solid;"
+ "-fx-border-color: black;-fx-border-width: 0 0 1 0");
for (int i = 0; i < 150; i++) {
VBox cell = new VBox();
cell.setMinWidth(40);
setCellStyle(cell);
gridPane.getChildren().add(cell);
}
return gridPane;
}
private void setCellStyle(VBox cell) {
String style = "-fx-border-style: solid;-fx-border-color: black;-fx-border-width: 0 1 0 0;";
cell.setStyle("-fx-background-color: null;" + style);
cell.setOnMouseEntered(e -> cell.setStyle("-fx-background-color: lightgray;" + style));
cell.setOnMouseExited(e -> cell.setStyle("-fx-background-color: null;" + style));
}
}
最后,这里是 AlignedButDoesNotScroll 的 class,它创建了一个示例,其中两个网格正确对齐,但当您向下滚动时,顶部网格不是固定在屏幕顶部。
import javafx.scene.control.ScrollPane;
import javafx.scene.control.ScrollPane.ScrollBarPolicy;
import javafx.scene.layout.HBox;
import javafx.scene.layout.Priority;
import javafx.scene.layout.VBox;
public class AlignedButDoesNotScroll extends VBox {
public AlignedButDoesNotScroll() {
super();
// Setting up the only scroll pane
ScrollPane gridScrollPane = new ScrollPane();
gridScrollPane.setHbarPolicy(ScrollBarPolicy.AS_NEEDED);
gridScrollPane.setVbarPolicy(ScrollBarPolicy.AS_NEEDED);
// Set up the content for the scroll pane
HBox beatNumbers = this.beatNumbers();
VBox layerGrid = new VBox();
for (int i = 0; i < 28; i++) {
layerGrid.getChildren().add(this.gridPane());
}
// Put everything together
VBox box = new VBox();
box.getChildren().addAll(beatNumbers, layerGrid);
gridScrollPane.setContent(box);
gridScrollPane.prefHeightProperty().bind(this.heightProperty());
this.getChildren().add(gridScrollPane);
}
private HBox beatNumbers() {
HBox beatNumbers = new HBox();
HBox.setHgrow(beatNumbers, Priority.ALWAYS);
for (int i = 0; i < 150; i++) {
VBox unit = new VBox();
unit.setMinWidth(40);
unit.setStyle("-fx-border-width: 0 1 0 0;-fx-border-color: green;");
Label measureNumber = new Label(String.valueOf(i + 1));
measureNumber.setStyle("-fx-text-fill: black");
unit.getChildren().add(measureNumber);
beatNumbers.getChildren().add(unit);
}
return beatNumbers;
}
private HBox gridPane() {
HBox gridPane = new HBox();
gridPane.setMinHeight(40);
HBox.setHgrow(gridPane, Priority.ALWAYS);
gridPane.setStyle("-fx-background-color: gray;-fx-border-style: solid;"
+ "-fx-border-color: black;-fx-border-width: 0 0 1 0");
for (int i = 0; i < 150; i++) {
VBox cell = new VBox();
cell.setMinWidth(40);
setCellStyle(cell);
gridPane.getChildren().add(cell);
}
return gridPane;
}
private void setCellStyle(VBox cell) {
String style = "-fx-border-style: solid;-fx-border-color: black;-fx-border-width: 0 1 0 0;";
cell.setStyle("-fx-background-color: null;" + style);
cell.setOnMouseEntered(e -> cell.setStyle("-fx-background-color: lightgray;" + style));
cell.setOnMouseExited(e -> cell.setStyle("-fx-background-color: null;" + style));
}
}
NotAlignedButScrolls
AlignedButDoesNotScroll
我希望这有助于解释问题。再次感谢!
问题是两个ScrollPanes的视口大小不一样。下方的较小以容纳滚动条。
这样的事情可以帮助...
gridScrollPane.viewportBoundsProperty().addListener((obs,oldVal, newVal) -> {
double w = newVal.getWidth() + 2; // this 2 accounts for 1 pixel borders
numberPane.setPrefWidth(w);
numberPane.setMaxWidth(w);
});
另一种选择是将两个 ScrollPanes 的 VBar 策略强制设置为 ALWAYS。不过看起来不太好。
我有两个包含要对齐的网格的滚动窗格。它们的单元格宽度都应为 40,所以我不确定它们为什么不对齐。尽管它们一开始看起来是对齐的,但它们逐渐变得不对齐。
Here, they are aligned.
But as you can see, it gradually becomes unaligned.
顶部网格(带有数字)和底部网格都包含在单独的滚动窗格中。如果我不这样做而是将两个网格放在一个 VBox 中并将其放入一个滚动窗格中,那么一切都会对齐。但是,我需要它们位于单独的滚动窗格中,以便我可以向下滚动下部网格,同时保持数字就位,并且数字仍然与网格一起水平滚动(我双向绑定了两个滚动窗格的 hscrollvalues)。
private void layerDisplay() {
BeatNumbers bn = new BeatNumbers(this.finalBeat, this.overlay, this::togglePlay,
this.features::setBeat);
VBox layerGrid = new VBox();
for (String layerName : this.layers.keySet()) {
LayerGrid lg = new LayerGrid(this.layers.get(layerName), layerName, this.finalBeat,
this.features::addUnit, this.features::deleteUnit, this.features::playUnit,
this.features::stopAllUnits, this.features::refresh);
layerGrid.getChildren().add(lg);
}
StackPane pane = new StackPane();
pane.getChildren().add(layerGrid);
pane.getChildren().add(this.overlay);
this.gridScrollPane.setContent(pane);
this.numberPane.setContent(bn);
VBox box = new VBox();
box.getChildren().addAll(this.numberPane, this.gridScrollPane);
this.setCenter(box);
}
你知道如何保持 BeatNumbers 和 layerGrid 对齐吗?当我向下滚动时,BeatNumbers 必须固定在屏幕顶部,但当我水平滚动时,它必须与网格一起水平移动。
谢谢!
编辑: 这是一个最小的可重现示例:
这是运行示例的代码
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;
public class MinimumReproducibleExample extends Application {
public static void main(String[] args) {
launch(args);
}
@Override
public void start(Stage stage) {
VBox notAlignedButScrolls = new NotAlignedButScrolls();
Scene scene = new Scene(notAlignedButScrolls, 400, 300);
stage.setTitle("Not Aligned But Scrolls");
stage.setScene(scene);
stage.show();
VBox alignedButDoesNotScroll = new AlignedButDoesNotScroll();
Scene scene2 = new Scene(alignedButDoesNotScroll, 400, 300);
Stage stage2 = new Stage();
stage2.setTitle("Aligned But Does Not Scroll");
stage2.setScene(scene2);
stage2.show();
}
}
这是 NotAlignedButScrolls 的 class,它创建了一个示例,其中两个网格按我希望的方式滚动,但数字逐渐与网格不对齐.
import javafx.scene.control.ScrollPane;
import javafx.scene.control.ScrollPane.ScrollBarPolicy;
import javafx.scene.input.ScrollEvent;
import javafx.scene.layout.HBox;
import javafx.scene.layout.Priority;
import javafx.scene.layout.VBox;
public class NotAlignedButScrolls extends VBox {
public NotAlignedButScrolls() {
super();
// Setting up the scroll panes
// This is the top scroll pane
ScrollPane numberPane = new ScrollPane();
numberPane.addEventFilter(ScrollEvent.SCROLL, event -> {
if (event.getDeltaY() != 0) {
event.consume();
}
});
numberPane.setHbarPolicy(ScrollBarPolicy.NEVER);
numberPane.setVbarPolicy(ScrollBarPolicy.NEVER);
numberPane.setMaxHeight(15);
numberPane.setMinHeight(15);
// This is the lower scroll pane
ScrollPane gridScrollPane = new ScrollPane();
gridScrollPane.setHbarPolicy(ScrollBarPolicy.AS_NEEDED);
gridScrollPane.setVbarPolicy(ScrollBarPolicy.AS_NEEDED);
// Bind their horizontal scroll values together
gridScrollPane.hvalueProperty().bindBidirectional(numberPane.hvalueProperty());
// Set up the content for each scroll pane
HBox beatNumbers = this.beatNumbers();
VBox layerGrid = new VBox();
for (int i = 0; i < 28; i++) {
layerGrid.getChildren().add(this.gridPane());
}
gridScrollPane.setContent(layerGrid);
numberPane.setContent(beatNumbers);
gridScrollPane.prefHeightProperty().bind(this.heightProperty());
// Put everything together
VBox box = new VBox();
box.getChildren().addAll(numberPane, gridScrollPane);
this.getChildren().add(box);
}
private HBox beatNumbers() {
HBox beatNumbers = new HBox();
HBox.setHgrow(beatNumbers, Priority.ALWAYS);
for (int i = 0; i < 150; i++) {
VBox unit = new VBox();
unit.setMinWidth(40);
unit.setStyle("-fx-border-width: 0 1 0 0;-fx-border-color: green;");
Label measureNumber = new Label(String.valueOf(i + 1));
measureNumber.setStyle("-fx-text-fill: black");
unit.getChildren().add(measureNumber);
beatNumbers.getChildren().add(unit);
}
return beatNumbers;
}
private HBox gridPane() {
HBox gridPane = new HBox();
gridPane.setMinHeight(40);
HBox.setHgrow(gridPane, Priority.ALWAYS);
gridPane.setStyle("-fx-background-color: gray;-fx-border-style: solid;"
+ "-fx-border-color: black;-fx-border-width: 0 0 1 0");
for (int i = 0; i < 150; i++) {
VBox cell = new VBox();
cell.setMinWidth(40);
setCellStyle(cell);
gridPane.getChildren().add(cell);
}
return gridPane;
}
private void setCellStyle(VBox cell) {
String style = "-fx-border-style: solid;-fx-border-color: black;-fx-border-width: 0 1 0 0;";
cell.setStyle("-fx-background-color: null;" + style);
cell.setOnMouseEntered(e -> cell.setStyle("-fx-background-color: lightgray;" + style));
cell.setOnMouseExited(e -> cell.setStyle("-fx-background-color: null;" + style));
}
}
最后,这里是 AlignedButDoesNotScroll 的 class,它创建了一个示例,其中两个网格正确对齐,但当您向下滚动时,顶部网格不是固定在屏幕顶部。
import javafx.scene.control.ScrollPane;
import javafx.scene.control.ScrollPane.ScrollBarPolicy;
import javafx.scene.layout.HBox;
import javafx.scene.layout.Priority;
import javafx.scene.layout.VBox;
public class AlignedButDoesNotScroll extends VBox {
public AlignedButDoesNotScroll() {
super();
// Setting up the only scroll pane
ScrollPane gridScrollPane = new ScrollPane();
gridScrollPane.setHbarPolicy(ScrollBarPolicy.AS_NEEDED);
gridScrollPane.setVbarPolicy(ScrollBarPolicy.AS_NEEDED);
// Set up the content for the scroll pane
HBox beatNumbers = this.beatNumbers();
VBox layerGrid = new VBox();
for (int i = 0; i < 28; i++) {
layerGrid.getChildren().add(this.gridPane());
}
// Put everything together
VBox box = new VBox();
box.getChildren().addAll(beatNumbers, layerGrid);
gridScrollPane.setContent(box);
gridScrollPane.prefHeightProperty().bind(this.heightProperty());
this.getChildren().add(gridScrollPane);
}
private HBox beatNumbers() {
HBox beatNumbers = new HBox();
HBox.setHgrow(beatNumbers, Priority.ALWAYS);
for (int i = 0; i < 150; i++) {
VBox unit = new VBox();
unit.setMinWidth(40);
unit.setStyle("-fx-border-width: 0 1 0 0;-fx-border-color: green;");
Label measureNumber = new Label(String.valueOf(i + 1));
measureNumber.setStyle("-fx-text-fill: black");
unit.getChildren().add(measureNumber);
beatNumbers.getChildren().add(unit);
}
return beatNumbers;
}
private HBox gridPane() {
HBox gridPane = new HBox();
gridPane.setMinHeight(40);
HBox.setHgrow(gridPane, Priority.ALWAYS);
gridPane.setStyle("-fx-background-color: gray;-fx-border-style: solid;"
+ "-fx-border-color: black;-fx-border-width: 0 0 1 0");
for (int i = 0; i < 150; i++) {
VBox cell = new VBox();
cell.setMinWidth(40);
setCellStyle(cell);
gridPane.getChildren().add(cell);
}
return gridPane;
}
private void setCellStyle(VBox cell) {
String style = "-fx-border-style: solid;-fx-border-color: black;-fx-border-width: 0 1 0 0;";
cell.setStyle("-fx-background-color: null;" + style);
cell.setOnMouseEntered(e -> cell.setStyle("-fx-background-color: lightgray;" + style));
cell.setOnMouseExited(e -> cell.setStyle("-fx-background-color: null;" + style));
}
}
NotAlignedButScrolls
AlignedButDoesNotScroll
我希望这有助于解释问题。再次感谢!
问题是两个ScrollPanes的视口大小不一样。下方的较小以容纳滚动条。
这样的事情可以帮助...
gridScrollPane.viewportBoundsProperty().addListener((obs,oldVal, newVal) -> {
double w = newVal.getWidth() + 2; // this 2 accounts for 1 pixel borders
numberPane.setPrefWidth(w);
numberPane.setMaxWidth(w);
});
另一种选择是将两个 ScrollPanes 的 VBar 策略强制设置为 ALWAYS。不过看起来不太好。