如何使用 TabPane 作为内容根据需要获取滚动条?

How to get Scrollbars as needed with a TabPane as Content?

我在 ScrollPane 内的 Tabpane 内有一个 BorderPane。如果我删除 TabPane 并将 BorderPane 作为 ScrollPane 的内容,ScrollPane.ScrollBarPolicy.AS_NEEDED 确实有效。我如何让它与 TabPane 一起工作?

BorderPane 能够以某种方式告诉 ScrollPane 何时显示滚动条,而 TabPane 不能这样做。我查看了 Tabpane 的可用方法,但找不到任何用于调整大小的方法。

工作示例:

import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.ScrollPane;
import javafx.scene.control.Tab;
import javafx.scene.control.TabPane;
import javafx.scene.layout.*;
import javafx.stage.Stage;

public class FXApplication extends Application {

    private BorderPane border;
    private GridPane inner;
    private TabPane tabPane;

    @Override
    public void start(Stage primaryStage) {

        tabPane = new TabPane();
        Tab tab = new Tab("test");
        tabPane.getTabs().add(tab);

        border = new BorderPane();
        border.setCenter(innerGrid());
        tab.setContent(border);

        ScrollPane scp = new ScrollPane();
        scp.setFitToHeight(true);
        scp.setFitToWidth(true);
        scp.setVbarPolicy(ScrollPane.ScrollBarPolicy.AS_NEEDED);
        scp.setHbarPolicy(ScrollPane.ScrollBarPolicy.AS_NEEDED);

//        scp.setContent(border);  // this works
        scp.setContent(tabPane);   // this doesnt

        Scene s = new Scene(scp);
        primaryStage.setScene(s);
        primaryStage.show();
    }

    private GridPane innerGrid() {
        inner = new GridPane();

        for(int i=0; i<11 ;i++) {
            ColumnConstraints columnConstraints = new ColumnConstraints();
            columnConstraints.setHgrow(Priority.SOMETIMES);
            inner.getColumnConstraints().add(columnConstraints);

            RowConstraints rowConstraints = new RowConstraints();
            rowConstraints.setVgrow(Priority.SOMETIMES);
            inner.getRowConstraints().add(rowConstraints);
        }

        for(int i=0; i<100 ;i++) {
            inner.add(new Button("Button " + i), i/10, i%10);
        }

        return inner;
    }

    public static void main(String[] args) {
        FXApplication.launch(args);
    }

}

令人惊讶的是,AS_NEEDED 的确切行为是未指定的。我们所拥有的只是要查看的 ScrollPaneSkin。是否显示 (f.i.) 单杠的决定发生在它的私有方法 determineHorizontalSBVisible()

private boolean determineHorizontalSBVisible() {
    final ScrollPane sp = getSkinnable();

    if (Properties.IS_TOUCH_SUPPORTED) {
        return (tempVisibility && (nodeWidth > contentWidth));
    }
    else {
        // RT-17395: ScrollBarPolicy might be null. If so, treat it as "AS_NEEDED", which is the default
        ScrollBarPolicy hbarPolicy = sp.getHbarPolicy();
        return (ScrollBarPolicy.NEVER == hbarPolicy) ? false :
               ((ScrollBarPolicy.ALWAYS == hbarPolicy) ? true :
               ((sp.isFitToWidth() && scrollNode != null ? scrollNode.isResizable() : false) ?
               (nodeWidth > contentWidth && scrollNode.minWidth(-1) > contentWidth) : (nodeWidth > contentWidth)));
    }
}

此处的 nodeWidth 是内容节点的实际宽度 - 之前已根据节点的 min/max 宽度计算 - contentWidth 是可用于布局内容的宽度。

不可读的代码(对我来说 ;)在可调整大小的内容和适合 scrollPane 的内容区域的情况下归结为如果内容的实际宽度和最小宽度都大于可用宽度则返回 true。

minWidth 使您的上下文有所不同:BorderPane 有一个 min > 0,TabPane 有一个 min == 0,所以上面的方法总是 returns false。

反之:要让 hbar 与 TabPane 一起可见,它需要一分钟,f.i。通过将其与其首选项相关联:

tabPane.setMinWidth(Region.USE_PREF_SIZE);