Gluon ScrollPane 刷新问题
Gluon ScrollPane refresh issue
我在 Android 中有一个消息传递应用程序 运行,它的设置与 中的一样
顺序如下
<View>
<BorderPane>
<center>
<ScrollPane>
<content>
<VBox> //issue is here
</content>
<ScrollPane>
<center>
<bottom>
<TextField>
<bottom>
</BorderPane>
</View>
当我使用
添加 children 到 VBox 时
VBox.getChildren().add(TextLabel);
ScrollPane 获取新的 VBox 并将其显示在屏幕上。
但是,当我添加更多 children 当前屏幕适合的内容时,我通过设置 vvalueProperty();
滚动到 ScrollPane 的末尾
ScrollPane.vvalueProperty().bind(VBox.heightProperty());
(以上代码对于重现问题至关重要)
当 运行 它在计算机上时,这工作得很好,但在移动设备上我有一个奇怪的问题,当我添加的 children 多于屏幕上可以容纳的内容时,scrollPane 会掉落 VBox。当我点击 VBox 区域时,屏幕会刷新,我会在屏幕上看到所需的内容
Video demonstrating ScrollBar issue in gluon
为方便起见,我设置了以下颜色代码
滚动条 - 红色
VBox - 蓝色
作为绑定的替代方法,我也尝试了
ScrollBar.setVvalue(1.0);
setVvalue() 没有同样的问题,但另一方面,它没有在视图中显示最后一条消息。
现在我已经尝试了所有可能的组合,包括用 FlowPane 替换 VBox 并观察到相同的行为。
我可以在 Android 设备上重现您的问题。不知何故,正如上面评论中所讨论的,垂直滚动位置的绑定导致了一些竞争条件。
与其试图找出问题的原因,我宁愿删除绑定并提出一种不同的方法来获得所需的结果:在这种情况下绑定是一个非常强大的条件。
当你尝试做同样的传递时:
vBox.getChildren().add(label);
scrollPane.setVvalue(vBox.getHeight());
您已经注意到并提到滚动位置未正确更新并且您遗漏了添加到 vBox 的最后一项。
这很容易解释:当您向框中添加新项目时,会调用 layoutChildren()
,这需要一些时间才能执行。至少需要另一个脉冲才能获得正确的值。
但是由于您尝试立即设置垂直滚动位置,因此值 vBox.getHeight()
仍将是 return 旧值。换句话说,您需要稍等片刻才能获得新值。
有几种方法可以做到这一点。最直接的是用一个监听器来监听盒子的 height
属性:
vBox.heightProperty().addListener((obs, ov, nv) ->
scrollPane.setVvalue(nv.doubleValue()));
作为替代方案,将项目添加到框中后,您可以使用:
vBox.getChildren().add(label);
Platform.runLater(() -> scrollPane.setVvalue(vBox.getHeight()));
但这并不能保证调用不会立即完成。所以最好做一个 PauseTransition
来代替,你可以控制时间:
vBox.getChildren().add(label);
PauseTransition pause = new PauseTransition(Duration.millis(30));
pause.setOnFinished(f -> scrollPane.setVvalue(vBox.getHeight()));
pause.play();
作为建议,您也可以做一个漂亮的过渡以滑入新项目。
备选方案
到目前为止,您正在使用 ScrollPane
结合 VBox
添加多个项目,允许滚动到列表中的第一个项目,但始终将滚动位置保持在底部所以最后添加的项目是完全可见的。虽然这工作正常(根据我上面的建议以避免绑定),但您正在向非虚拟化容器添加许多节点。
我认为有一个更好的选择,ListView
(或者更好的 CharmListView
允许 headers)。使用适当的 CellFactory
,您可以获得完全相同的视觉效果,并且可以直接滚动到最后一项。但此控件的主要优点是它的 virtualFlow,当您将更多项目添加到列表时,它将为您管理有限数量的节点。
这只是一个简短的代码片段,用于为您的聊天应用程序使用 ListView
控件:
ListView<String> listView = new ListView<>();
listView.setCellFactory(p -> new ListCell<String>() {
private final Label label;
{
label = new Label(null, MaterialDesignIcon.CHAT_BUBBLE.graphic());
label.setMaxWidth(Double.MAX_VALUE);
label.setPrefWidth(this.getWidth() - 60);
label.setPrefHeight(30);
}
@Override
protected void updateItem(String item, boolean empty) {
super.updateItem(item, empty);
if (item != null && ! empty) {
label.setText(item);
label.setAlignment(getIndex() % 2 == 0 ? Pos.CENTER_LEFT : Pos.CENTER_RIGHT);
setGraphic(label);
} else {
setGraphic(null);
}
}
});
setCenter(listView);
要添加新项目并滚动到它,您只需要:
listView.getItems().add("Text " + (listView.getItems().size() + 1));
listView.scrollTo(listView.getItems().size() - 1);
当然,通过适当的样式,您可以删除行之间的线条,并创建与 scrollPane 相同的视觉效果。
我在 Android 中有一个消息传递应用程序 运行,它的设置与
<View>
<BorderPane>
<center>
<ScrollPane>
<content>
<VBox> //issue is here
</content>
<ScrollPane>
<center>
<bottom>
<TextField>
<bottom>
</BorderPane>
</View>
当我使用
添加 children 到 VBox 时VBox.getChildren().add(TextLabel);
ScrollPane 获取新的 VBox 并将其显示在屏幕上。 但是,当我添加更多 children 当前屏幕适合的内容时,我通过设置 vvalueProperty();
滚动到 ScrollPane 的末尾ScrollPane.vvalueProperty().bind(VBox.heightProperty());
(以上代码对于重现问题至关重要)
当 运行 它在计算机上时,这工作得很好,但在移动设备上我有一个奇怪的问题,当我添加的 children 多于屏幕上可以容纳的内容时,scrollPane 会掉落 VBox。当我点击 VBox 区域时,屏幕会刷新,我会在屏幕上看到所需的内容 Video demonstrating ScrollBar issue in gluon
为方便起见,我设置了以下颜色代码
滚动条 - 红色
VBox - 蓝色
作为绑定的替代方法,我也尝试了
ScrollBar.setVvalue(1.0);
setVvalue() 没有同样的问题,但另一方面,它没有在视图中显示最后一条消息。 现在我已经尝试了所有可能的组合,包括用 FlowPane 替换 VBox 并观察到相同的行为。
我可以在 Android 设备上重现您的问题。不知何故,正如上面评论中所讨论的,垂直滚动位置的绑定导致了一些竞争条件。
与其试图找出问题的原因,我宁愿删除绑定并提出一种不同的方法来获得所需的结果:在这种情况下绑定是一个非常强大的条件。
当你尝试做同样的传递时:
vBox.getChildren().add(label);
scrollPane.setVvalue(vBox.getHeight());
您已经注意到并提到滚动位置未正确更新并且您遗漏了添加到 vBox 的最后一项。
这很容易解释:当您向框中添加新项目时,会调用 layoutChildren()
,这需要一些时间才能执行。至少需要另一个脉冲才能获得正确的值。
但是由于您尝试立即设置垂直滚动位置,因此值 vBox.getHeight()
仍将是 return 旧值。换句话说,您需要稍等片刻才能获得新值。
有几种方法可以做到这一点。最直接的是用一个监听器来监听盒子的 height
属性:
vBox.heightProperty().addListener((obs, ov, nv) ->
scrollPane.setVvalue(nv.doubleValue()));
作为替代方案,将项目添加到框中后,您可以使用:
vBox.getChildren().add(label);
Platform.runLater(() -> scrollPane.setVvalue(vBox.getHeight()));
但这并不能保证调用不会立即完成。所以最好做一个 PauseTransition
来代替,你可以控制时间:
vBox.getChildren().add(label);
PauseTransition pause = new PauseTransition(Duration.millis(30));
pause.setOnFinished(f -> scrollPane.setVvalue(vBox.getHeight()));
pause.play();
作为建议,您也可以做一个漂亮的过渡以滑入新项目。
备选方案
到目前为止,您正在使用 ScrollPane
结合 VBox
添加多个项目,允许滚动到列表中的第一个项目,但始终将滚动位置保持在底部所以最后添加的项目是完全可见的。虽然这工作正常(根据我上面的建议以避免绑定),但您正在向非虚拟化容器添加许多节点。
我认为有一个更好的选择,ListView
(或者更好的 CharmListView
允许 headers)。使用适当的 CellFactory
,您可以获得完全相同的视觉效果,并且可以直接滚动到最后一项。但此控件的主要优点是它的 virtualFlow,当您将更多项目添加到列表时,它将为您管理有限数量的节点。
这只是一个简短的代码片段,用于为您的聊天应用程序使用 ListView
控件:
ListView<String> listView = new ListView<>();
listView.setCellFactory(p -> new ListCell<String>() {
private final Label label;
{
label = new Label(null, MaterialDesignIcon.CHAT_BUBBLE.graphic());
label.setMaxWidth(Double.MAX_VALUE);
label.setPrefWidth(this.getWidth() - 60);
label.setPrefHeight(30);
}
@Override
protected void updateItem(String item, boolean empty) {
super.updateItem(item, empty);
if (item != null && ! empty) {
label.setText(item);
label.setAlignment(getIndex() % 2 == 0 ? Pos.CENTER_LEFT : Pos.CENTER_RIGHT);
setGraphic(label);
} else {
setGraphic(null);
}
}
});
setCenter(listView);
要添加新项目并滚动到它,您只需要:
listView.getItems().add("Text " + (listView.getItems().size() + 1));
listView.scrollTo(listView.getItems().size() - 1);
当然,通过适当的样式,您可以删除行之间的线条,并创建与 scrollPane 相同的视觉效果。