在JavaFX中,如何让ScrollPane的内容保持在视口的边界内?

5

我意识到我不可能是第一个希望让ScrollPane的内容保持在视口边框内的人,但是经过几个小时的搜索,我没有找到答案。

我用各种搜索词在Google、you.com和duckduckgo上搜索,例如:

  • JavaFX ScrollPane 视口限制内容仅在边框内
  • JavaFX ScrollPane 视口保持内容在视口边框内

以及其他类似措辞的搜索词,但我没有找到答案。

我创建了基本的可重现代码,准确地模拟了我在实际程序中呈现的数据。它实际上非常简单:

  • 我有多个VBox,每个VBox都有三个标签和一个分隔符。
  • 每个VBox都添加到父VBox中。
  • 那个父VBox被添加到一个ScrollPane中。
  • ScrollPane使用一些轻微的样式去除了其滚动条。

问题:滚动时,ScrollPane的内容会重叠在ScrollPane视口的边框上,我无法找出如何告诉ScrollPane将视口的内容保持在其边框内。

如果有其他方法可以使可滚动内容保持在视口边界内,我不知道,希望得到任何指导。我的假设是,视口的实际边框显然可以定义其宽度和颜色等,但它恰好被“绘制”在视口实际内容的相同空间内。因此,不应该认为边框是任何预期边界,以便随后将内容限制在其中...如果这是真的,我当然接受,但这并不能帮助我实现我想要的行为,即确保内容不显示在视口边界上方。在这里,您可以看到我所说的内容。

enter image description here

这里是展示行为的代码:

import javafx.application.Application;
import javafx.geometry.HPos;
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.control.Separator;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;
import javafx.stage.StageStyle;

import java.util.Random;

public class Main extends Application {

    @Override public void start(Stage stage) throws Exception {
        random = new Random(System.currentTimeMillis());
        makeScrollPane();
        Scene scene = new Scene(scrollPane);
        stage.initStyle(StageStyle.TRANSPARENT);
        stage.setScene(scene);
        stage.centerOnScreen();
        stage.setWidth(width);
        stage.setHeight(height);
        stage.show();

    }
    private       Random     random;
    private final double     width       = 300;
    private final double     height      = 500;
    private       ScrollPane scrollPane;
    private final String     scrollStyle = Main.class.getResource("scrollpane.css").toExternalForm();
    private final String     labelStyle  = Main.class.getResource("label.css").toExternalForm();

    private void makeScrollPane() {
        VBox[] contentArray = new VBox[15];
        for (int x = 0; x < 15; x++) {
            String boxIndex = String.valueOf(random());
            String boxNum = String.valueOf(random());
            String lbl1   = "Title " + boxIndex;
            String lbl2   = "Item " + boxIndex;
            contentArray[x] = getVBox(lbl1, lbl2, boxNum);
        }
        VBox vBoxContent = new VBox(contentArray);
        scrollPane = new ScrollPane(vBoxContent);
        scrollPane.fitToWidthProperty().set(true);
        scrollPane.fitToHeightProperty().set(true);
        scrollPane.getStylesheets().add(scrollStyle);
        scrollPane.hbarPolicyProperty().set(ScrollPane.ScrollBarPolicy.NEVER);
        scrollPane.vbarPolicyProperty().set(ScrollPane.ScrollBarPolicy.NEVER);
    }

    private VBox getVBox(String label1, String label2, String label3) {
        Label lbl1 = newLabel(label1);
        Label lbl2 = newLabel(label2);
        Label lbl3 = newLabel(label3);
        lbl3.setId("big");
        Separator separator = new Separator();
        separator.setMaxWidth(250);
        separator.setHalignment(HPos.CENTER);
        separator.setPadding(new Insets(0, 0, 25, 0));
        VBox vBox = new VBox(20, lbl1, lbl2, lbl3, separator);
        vBox.setPrefWidth(width * .9);
        vBox.setPrefHeight(100);
        vBox.setAlignment(Pos.CENTER);
        return vBox;
    }

    private int random() {
        return random.nextInt(1000, 9999);
    }

    private Label newLabel(String content) {
        Label label = new Label(content);
        label.getStylesheets().add(labelStyle);
        label.setPrefWidth(width * .9);
        label.setPrefHeight(25);
        label.setAlignment(Pos.CENTER);
        return label;
    }
};

还有两个与之配套的样式表:

label.css

.label {
    -fx-font-size: 13pt;
    -fx-background-color: transparent;
    -fx-font-family: "Arial";
    -fx-text-fill: ghostwhite;
}

#big {
    -fx-font-size: 40;
    -fx-text-fill: yellow;
}

scrollpane.css

.scroll-pane .viewport {
    -fx-background-color: black;
    -fx-border-color: darkorange;
    -fx-border-width: .7em;
}

.root {
    -fx-background-color: black;
}

我希望能得到一些指导,以找到实现我所追求的目标的方法。

谢谢


3
有趣。看起来视口不考虑填充,这将是我在这里采取的第一个方法。是否有一种解决方案,将边框放在滚动窗格本身上,并在视口中使用黑色背景,这样可用吗?我认为这将在您提供的演示应用程序中起作用。 - James_D
3
在我看来似乎是一个bug:viewport被指定为ScrollPane的子结构,因此应该完全可样式化,皮肤应该遵守这些样式。在这种情况下,对ScrollPane本身进行样式设置(如@James_D建议的那样)似乎可以起作用。 - kleopatra
@James_D - 我也尝试过在 scrollPane 中给 VBox 加填充,但正如你所意识到的那样,填充只会考虑到 VBox 的边缘,而顶部和底部会超出视口边界。我将 stylesheet.css 更改为仅将边框应用于 scroll-pane 本身,这样可以正常工作。想知道是否应提交此问题报告? - Michael Sims
您可以检查是否已存在错误报告。否则,我会提交错误并看看他们的回复。 - Slaw
1个回答

3
在这种情况下的解决方案(我想这在技术上算是一个解决方法)是只对ScrollPane本身进行边框样式设置,而将视口保留为黑色背景,正如James_D建议的那样。
仅需更改scrollpane.css即可解决问题:
.scroll-pane {
    -fx-background-color: black;
    -fx-border-color: darkorange;
    -fx-border-width: .7em;
}

.scroll-pane .viewport {
    -fx-background-color: black;
}

.root {
    -fx-background-color: black;
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接