Javafx 胶子滚动窗格刷新问题

Javafx 胶子滚动窗格刷新问题,javafx,gluon,gluon-mobile,javafxports,Javafx,Gluon,Gluon Mobile,Javafxports,我有一个在Android上运行的消息传递应用程序,它的设置与 顺序如下 <View> <BorderPane> <center> <ScrollPane> <content> <VBox> //issue is here </content> <ScrollPane> <center> <bottom>

我有一个在Android上运行的消息传递应用程序,它的设置与 顺序如下

<View>
<BorderPane>
  <center>
    <ScrollPane>
       <content>
         <VBox> //issue is here
       </content>
    <ScrollPane>
  <center>
  <bottom>
    <TextField>
  <bottom>
</BorderPane>
</View>
滚动窗格将获得新的VBox并在屏幕上显示。 然而,当我添加更多的子屏幕时,我会通过设置vvalueProperty()滚动到滚动窗格的末尾

(上述代码对于重新创建问题至关重要)

这在电脑上运行时效果非常好,但在移动设备上,我遇到了一个奇怪的问题,当我添加的子项超过屏幕上所能容纳的数量时,scrollPane会删除VBox。当我点击VBox区域时,屏幕会刷新,并在屏幕上获得所需的内容

为方便起见,我设置了以下颜色代码

滚动条-红色

VBox-蓝

作为绑定的替代方案,我也尝试了

 ScrollBar.setVvalue(1.0);
setVvalue()没有相同的问题,但另一方面,它没有在视图中显示最后一条消息。
现在我已经尝试了所有可能的组合,包括用FlowPane替换VBox,并观察到了相同的行为。

我可以在Android设备上重现您的问题。不知何故,正如上面评论中所讨论的,垂直滚动位置的绑定导致了一些竞争条件

与其试图找出问题的原因,我宁愿删除绑定并提出一种不同的方法来获得期望的结果:在这种情况下,绑定是一个非常强的条件

当您尝试在同一关卡中执行此操作时:

vBox.getChildren().add(label);
scrollPane.setVvalue(vBox.getHeight());
您已经注意到并提到滚动位置未正确更新,并且缺少添加到vBox的最后一项

这很容易解释:当您向框中添加新项时,将调用
layoutChildren()
,这将需要一些时间才能执行。至少需要另一个脉冲才能得到正确的值

但由于您试图立即设置垂直滚动位置,因此值
vBox.getHeight()
仍将返回旧值。换句话说,您必须等待一段时间才能获得新值

有几种方法可以做到这一点。最直接的方法是使用框的
height
属性的侦听器:

vBox.heightProperty().addListener((obs, ov, nv) -> 
    scrollPane.setVvalue(nv.doubleValue()));
或者,将项目添加到方框后,您可以使用:

vBox.getChildren().add(label);
Platform.runLater(() -> scrollPane.setVvalue(vBox.getHeight()));
但这并不能保证电话不会立即接通。因此,最好改为执行
暂停转换
,这样可以控制计时:

vBox.getChildren().add(label);

PauseTransition pause = new PauseTransition(Duration.millis(30));
pause.setOnFinished(f -> scrollPane.setVvalue(vBox.getHeight()));
pause.play();
作为一个建议,您还可以做一个很好的过渡,以滑入新项目

替代解决方案

到目前为止,您正在使用一个
滚动窗格
和一个
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);


当然,通过适当的样式,您可以删除行之间的行,并创建与滚动窗格相同的视觉效果。

所有这些都是在JavaFx线程上运行的吗?另外,您是否在其他移动设备上试用过?是的,它在JavaFX线程上运行,我也在不同的android设备上试用过,但有相同的问题。您在视频中发送的某些文本没有出现在vbox中,这有何原因?这可能是一个潜在的问题导致了这一点。最初的几个文本在滚动窗格的顶部,当我打开键盘时,它们不在视图中,如果我关闭键盘视图,它们可以被看到。我也需要弄清楚,要记住那些文字@Hypnic JerkI创建了一个类似的小应用程序,在滚动窗格中的VBox中放置了一个标签,我没有droppage。我没有尝试设置任何Vvalue或绑定该属性。我还使用了Glion内置视图,而不是BorderPane。我看不到你的代码了,我没有主意了。谢谢!,移动到列表视图,其工作正常
vBox.getChildren().add(label);

PauseTransition pause = new PauseTransition(Duration.millis(30));
pause.setOnFinished(f -> scrollPane.setVvalue(vBox.getHeight()));
pause.play();
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);