我有一个在
Android中运行的消息传递应用程序,其设置类似于
setup of the screen
订单如下
<View>
<BorderPane>
<center>
<ScrollPane>
<content>
<VBox> //issue is here
</content>
<ScrollPane>
<center>
<bottom>
<TextField>
<bottom>
</BorderPane>
</View>
当我将孩子添加到VBox时
VBox.getChildren().add(TextLabel);
ScrollPane获取新的VBox并在屏幕上显示.
然而,当我添加更多的孩子时,当前屏幕可以适合我通过设置vvalueProperty()滚动到ScrollPane的结尾;
ScrollPane.vvalueProperty().bind(VBox.heightProperty());
(以上代码对于重新创建问题至关重要)
这在计算机上运行时完全正常,但在移动设备上我有这个奇怪的问题,当我添加更多的孩子而不是屏幕上可以放置的时候,scrollPane会丢弃VBox.当我点击VBox区域时,屏幕刷新,我在屏幕上获得所需的内容
Video demonstrating ScrollBar issue in gluon
为方便起见,我设置了以下颜色代码
ScrollBar – 红色
VBox – 蓝色
作为绑定的替代,我也尝试过
ScrollBar.setVvalue(1.0);
setVvalue()没有相同的问题,但另一方面,这没有显示视图中的最后一条消息.
现在我已经尝试了所有可能的组合,包括用FlowPane替换VBox并观察到相同的行为.
最佳答案 我可以在Android设备上重现您的问题.不知何故,如上面的评论中所讨论的,垂直滚动位置的绑定导致一些竞争条件.
我宁愿删除绑定并提出一种不同的方法来获得所需的结果,而不是试图找出问题的原因:在这种情况下,绑定是一个非常强大的条件.
当你尝试在同一个传递中执行此操作时:
vBox.getChildren().add(label);
scrollPane.setVvalue(vBox.getHeight());
你已经注意到并提到滚动位置没有正确更新,你错过了添加到vBox的最后一项.
这可以很容易地解释:当您向框中添加新项目时,会调用layoutChildren(),这将需要一些时间来执行.至少需要另一个脉冲来获得正确的值.
但是,由于您尝试立即设置垂直滚动位置,值vBox.getHeight()仍将返回旧值.换句话说,您必须等待一点才能获得新值.
有几种方法可以做到这一点.最简单的是监听盒子的高度属性:
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,将允许标题).使用正确的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相同的视觉效果.