JavaFX ScrollPane - 检测滚动条何时可见?
JavaFX ScrollPane - Detect when scrollbar is visible?
我有一个 ScrollPane 如下:
ScrollPane scroller = new ScrollPane();
scroller.getStyleClass().add("scroller");
scroller.setPrefWidth(width);
scroller.setFocusTraversable(Boolean.FALSE);
scroller.setPannable(Boolean.TRUE);
scroller.setFitToWidth(Boolean.TRUE);
scroller.setHbarPolicy(ScrollPane.ScrollBarPolicy.NEVER);
scroller.setVbarPolicy(ScrollPane.ScrollBarPolicy.AS_NEEDED);
this.setCenter(scroller);
scroller.contentProperty().addListener((observableValue, last, now) ->
{
ScrollBar scrollBar = (ScrollBar) scroller.lookup(".scroll-bar:vertical");
if (scrollBar != null)
{
if (scrollBar.isVisible())
{
log.info("Scrollbar visible, setting lower card width..");
}
else
{
log.info("Scrollbar not visible, setting default card width..");
}
}
});
如您所见,我已将侦听器附加到内容 属性 以了解内容何时设置。我试图查看内容更新时滚动条是否可见。尽管我可以在 UI 上看到滚动条,但它总是转到其他部分 - “滚动条不可见”。
不确定是否有其他方法可以做到这一点?检查了很多 Whosebug 和 Oracle 文档 - 没有发现任何可靠的建议。
-- 为问题添加上下文以更好地理解:
只是想解释一下问题是什么,不确定是应该把它作为回复评论还是编辑问题,请指教并会更改它:
所以我有这个视图,它从 Firebase 中调出需要加载到 TilePane 上的记录,该 TilePane 托管在 ScrollPane 中,进入 BorderPane 的中心。
我从 Firebase 获得响应的时间无法预测,因为它是异步的。因此 UI 加载了空的 TilePane,然后异步调用去获取数据。当数据可用时,我需要准备卡片(即 HBox),但列数是固定的。所以不得不调整卡片的宽度,保持TilePane上的gap(16px)和padding(16px)一致,同时保持5列。每张卡片的宽度需要根据显示是否有滚动条重新计算。因为如果显示滚动条,它需要一些 space 并且 TilePane 会将它缩小到 4 列,留下很多空 space。如果不清楚,很乐意进一步解释。
我强烈建议遵循评论中给出的建议。关键在于选择正确的布局。
我回答这个问题的目的是,如果将来有人遇到这个关于处理滚动条可见性的问题,他们至少会知道一种方法(在 JavaFX 8).
检查滚动条可见性的一种方法是在 layoutChildren 上注册适当的滚动条并为其可见性添加一个侦听器 属性。像...
ScrollPane scrollPane = new ScrollPane() {
ScrollBar vertical;
@Override
protected void layoutChildren() {
super.layoutChildren();
if (vertical == null) {
vertical = (ScrollBar) lookup(".scroll-bar:vertical");
vertical.visibleProperty().addListener((obs, old, val) -> updateContent(val));
updateContent(vertical.isVisible());
}
}
};
updateContent(visible)
方法是您在更新可见性时想要执行的操作。
一个完整的工作演示如下。
import javafx.application.Application;
import javafx.geometry.Insets;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.Label;
import javafx.scene.control.ScrollBar;
import javafx.scene.control.ScrollPane;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.HBox;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;
public class ScrollPaneScrollBarVisibility_Demo extends Application {
@Override
public void start(Stage stage) throws Exception {
BorderPane borderPane = new BorderPane();
Scene sc = new Scene(borderPane, 300, 300);
stage.setScene(sc);
stage.setTitle("ScrollBar visibility");
stage.show();
ScrollPane scrollPane = new ScrollPane() {
ScrollBar vertical;
@Override
protected void layoutChildren() {
super.layoutChildren();
if (vertical == null) {
vertical = (ScrollBar) lookup(".scroll-bar:vertical");
vertical.visibleProperty().addListener((obs, old, val) -> updateContent(val));
updateContent(vertical.isVisible());
}
}
};
scrollPane.setContent(getContent());
borderPane.setCenter(scrollPane);
}
private void updateContent(boolean scrollBarVisible) {
System.out.println("Vertical scroll bar visible :: " + scrollBarVisible);
}
private VBox getContent() {
VBox labels = new VBox();
labels.setSpacing(5);
for (int i = 0; i < 10; i++) {
labels.getChildren().add(new Label("X " + i));
}
Button add = new Button("Add");
add.setOnAction(e -> labels.getChildren().add(new Label("Text")));
Button remove = new Button("Remove");
remove.setOnAction(e -> {
if (!labels.getChildren().isEmpty()) {
labels.getChildren().remove(labels.getChildren().size() - 1);
}
});
HBox buttons = new HBox(add, remove);
buttons.setSpacing(15);
VBox content = new VBox(buttons, labels);
content.setPadding(new Insets(15));
content.setSpacing(15);
return content;
}
public static void main(String[] args) {
Application.launch(args);
}
}
正如@James_D所说,使用了 GridPane 并且它在没有任何监听器的情况下工作:
GridPane cards = new GridPane();
cards.setVgap(16);
cards.setHgap(16);
cards.setAlignment(Pos.CENTER);
cards.setPadding(new Insets(16));
ColumnConstraints constraints = new ColumnConstraints();
constraints.setPercentWidth(20);
constraints.setHgrow(Priority.ALWAYS);
constraints.setFillWidth(Boolean.TRUE);
cards.getColumnConstraints().addAll(constraints, constraints, constraints, constraints, constraints);
我有 5 列,所以有 5 次约束。工作得很好。
我有一个 ScrollPane 如下:
ScrollPane scroller = new ScrollPane();
scroller.getStyleClass().add("scroller");
scroller.setPrefWidth(width);
scroller.setFocusTraversable(Boolean.FALSE);
scroller.setPannable(Boolean.TRUE);
scroller.setFitToWidth(Boolean.TRUE);
scroller.setHbarPolicy(ScrollPane.ScrollBarPolicy.NEVER);
scroller.setVbarPolicy(ScrollPane.ScrollBarPolicy.AS_NEEDED);
this.setCenter(scroller);
scroller.contentProperty().addListener((observableValue, last, now) ->
{
ScrollBar scrollBar = (ScrollBar) scroller.lookup(".scroll-bar:vertical");
if (scrollBar != null)
{
if (scrollBar.isVisible())
{
log.info("Scrollbar visible, setting lower card width..");
}
else
{
log.info("Scrollbar not visible, setting default card width..");
}
}
});
如您所见,我已将侦听器附加到内容 属性 以了解内容何时设置。我试图查看内容更新时滚动条是否可见。尽管我可以在 UI 上看到滚动条,但它总是转到其他部分 - “滚动条不可见”。
不确定是否有其他方法可以做到这一点?检查了很多 Whosebug 和 Oracle 文档 - 没有发现任何可靠的建议。
-- 为问题添加上下文以更好地理解:
只是想解释一下问题是什么,不确定是应该把它作为回复评论还是编辑问题,请指教并会更改它:
所以我有这个视图,它从 Firebase 中调出需要加载到 TilePane 上的记录,该 TilePane 托管在 ScrollPane 中,进入 BorderPane 的中心。
我从 Firebase 获得响应的时间无法预测,因为它是异步的。因此 UI 加载了空的 TilePane,然后异步调用去获取数据。当数据可用时,我需要准备卡片(即 HBox),但列数是固定的。所以不得不调整卡片的宽度,保持TilePane上的gap(16px)和padding(16px)一致,同时保持5列。每张卡片的宽度需要根据显示是否有滚动条重新计算。因为如果显示滚动条,它需要一些 space 并且 TilePane 会将它缩小到 4 列,留下很多空 space。如果不清楚,很乐意进一步解释。
我强烈建议遵循评论中给出的建议。关键在于选择正确的布局。
我回答这个问题的目的是,如果将来有人遇到这个关于处理滚动条可见性的问题,他们至少会知道一种方法(在 JavaFX 8).
检查滚动条可见性的一种方法是在 layoutChildren 上注册适当的滚动条并为其可见性添加一个侦听器 属性。像...
ScrollPane scrollPane = new ScrollPane() {
ScrollBar vertical;
@Override
protected void layoutChildren() {
super.layoutChildren();
if (vertical == null) {
vertical = (ScrollBar) lookup(".scroll-bar:vertical");
vertical.visibleProperty().addListener((obs, old, val) -> updateContent(val));
updateContent(vertical.isVisible());
}
}
};
updateContent(visible)
方法是您在更新可见性时想要执行的操作。
一个完整的工作演示如下。
import javafx.application.Application;
import javafx.geometry.Insets;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.Label;
import javafx.scene.control.ScrollBar;
import javafx.scene.control.ScrollPane;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.HBox;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;
public class ScrollPaneScrollBarVisibility_Demo extends Application {
@Override
public void start(Stage stage) throws Exception {
BorderPane borderPane = new BorderPane();
Scene sc = new Scene(borderPane, 300, 300);
stage.setScene(sc);
stage.setTitle("ScrollBar visibility");
stage.show();
ScrollPane scrollPane = new ScrollPane() {
ScrollBar vertical;
@Override
protected void layoutChildren() {
super.layoutChildren();
if (vertical == null) {
vertical = (ScrollBar) lookup(".scroll-bar:vertical");
vertical.visibleProperty().addListener((obs, old, val) -> updateContent(val));
updateContent(vertical.isVisible());
}
}
};
scrollPane.setContent(getContent());
borderPane.setCenter(scrollPane);
}
private void updateContent(boolean scrollBarVisible) {
System.out.println("Vertical scroll bar visible :: " + scrollBarVisible);
}
private VBox getContent() {
VBox labels = new VBox();
labels.setSpacing(5);
for (int i = 0; i < 10; i++) {
labels.getChildren().add(new Label("X " + i));
}
Button add = new Button("Add");
add.setOnAction(e -> labels.getChildren().add(new Label("Text")));
Button remove = new Button("Remove");
remove.setOnAction(e -> {
if (!labels.getChildren().isEmpty()) {
labels.getChildren().remove(labels.getChildren().size() - 1);
}
});
HBox buttons = new HBox(add, remove);
buttons.setSpacing(15);
VBox content = new VBox(buttons, labels);
content.setPadding(new Insets(15));
content.setSpacing(15);
return content;
}
public static void main(String[] args) {
Application.launch(args);
}
}
正如@James_D所说,使用了 GridPane 并且它在没有任何监听器的情况下工作:
GridPane cards = new GridPane();
cards.setVgap(16);
cards.setHgap(16);
cards.setAlignment(Pos.CENTER);
cards.setPadding(new Insets(16));
ColumnConstraints constraints = new ColumnConstraints();
constraints.setPercentWidth(20);
constraints.setHgrow(Priority.ALWAYS);
constraints.setFillWidth(Boolean.TRUE);
cards.getColumnConstraints().addAll(constraints, constraints, constraints, constraints, constraints);
我有 5 列,所以有 5 次约束。工作得很好。