加载 in/displaying FXML 时避免动画中的短暂卡顿
Avoiding short stutter in animation when loading in/displaying FXML
当加载新的 FXML 并设置 BorderPane 的中心时,应用程序的简短 'freeze' 将停止现有动画,无论是来自时间轴还是来自图像视图中的 gif .我正在使用此代码更改 centerView:
@FXML
public void handleChangeView(ActionEvent event) {
Task<Parent> loadTask = new Task<>() {
@Override
public Parent call() throws IOException {
String changeButtonID = ((ToggleButton) event.getSource()).getId();
Parent newOne = getFxmls().get(changeButtonID);
if (newOne == null) {
FXMLLoader loader = new FXMLLoader(getClass().getResource("/view/" + changeButtonID + ".fxml"));
newOne = loader.load();
getFxmls().put(changeButtonID, newOne);
}
return newOne ;
}
};
loadTask.setOnSucceeded(e -> {
getMainUI().setCenter(loadTask.getValue());
});
loadTask.setOnFailed(e -> loadTask.getException().printStackTrace());
Thread thread = new Thread(loadTask);
thread.start();
}
虽然这样做可以保持 UI 在加载时保持响应,但当中心舞台首次显示时,会出现明显的滞后。查看 CPU 个人资料:
我不确定延迟是来自初始化函数 运行 还是元素加载。这是程序的 gif,您可以看到可见的延迟:
通过在 VLC 中加载它并逐帧进行,在 30 fps 视频中延迟看起来是 5 或 6 帧,这意味着大约 200 毫秒的延迟,这意味着动画冻结超过 50 毫秒左右初始化方法。 (也许整个加载方法都卡住了动画?)
问题是,在这期间是否可以保持动画流畅?
//********* 编辑 **********//
所以我检查了项目并尽可能多地删除了方法和类,将整个游戏减少到 6 个最小 类。我在按下字符按钮('you' 按钮)时仍有延迟。有了这样一个最小的例子,我就不知道可能出了什么问题。我已将其上传到 google 驱动器,供任何人查看。
https://drive.google.com/open?id=17A-PB2517bPJc8Dek-yp2wGXsjTyTj1d
您的代码 "wrong" 没有任何内容(至少在这个问题上)。
您遇到的问题也与 FXML 的加载无关(非常慢并且您已正确处理 FX-Thread)。
出现口吃的原因如下:
- 相对
character.fxml
中的大层次结构
- 很多 CSS(删除
main.css
,您会注意到;卡顿稍微不那么突出)
- 动态改变场景图(运行时 adding/removing 个节点)
每次用某个大节点替换 mainView
的 center
时,都会导致 JavaFx 运行时完全重新布局并(至少)重新设置该节点的样式。这发生在 FX-Thread 上,因此您注意到卡顿。
一种可能的缓解方法是经典的游戏开发技术:尽可能多地预分配。
只需在启动期间加载一次所有必需的 FMXL,然后将它们放入场景图中。然后在您的点击处理程序中,将您想要的节点的可见性或 (Z-) 位置更改为 show/hide。
例如,这是 StackPane
的一个很好的用例。
我稍微调整了您的代码以证明我的意思:
Prototype
查看这些资源以了解更多信息:
当加载新的 FXML 并设置 BorderPane 的中心时,应用程序的简短 'freeze' 将停止现有动画,无论是来自时间轴还是来自图像视图中的 gif .我正在使用此代码更改 centerView:
@FXML
public void handleChangeView(ActionEvent event) {
Task<Parent> loadTask = new Task<>() {
@Override
public Parent call() throws IOException {
String changeButtonID = ((ToggleButton) event.getSource()).getId();
Parent newOne = getFxmls().get(changeButtonID);
if (newOne == null) {
FXMLLoader loader = new FXMLLoader(getClass().getResource("/view/" + changeButtonID + ".fxml"));
newOne = loader.load();
getFxmls().put(changeButtonID, newOne);
}
return newOne ;
}
};
loadTask.setOnSucceeded(e -> {
getMainUI().setCenter(loadTask.getValue());
});
loadTask.setOnFailed(e -> loadTask.getException().printStackTrace());
Thread thread = new Thread(loadTask);
thread.start();
}
虽然这样做可以保持 UI 在加载时保持响应,但当中心舞台首次显示时,会出现明显的滞后。查看 CPU 个人资料:
我不确定延迟是来自初始化函数 运行 还是元素加载。这是程序的 gif,您可以看到可见的延迟:
通过在 VLC 中加载它并逐帧进行,在 30 fps 视频中延迟看起来是 5 或 6 帧,这意味着大约 200 毫秒的延迟,这意味着动画冻结超过 50 毫秒左右初始化方法。 (也许整个加载方法都卡住了动画?)
问题是,在这期间是否可以保持动画流畅?
//********* 编辑 **********//
所以我检查了项目并尽可能多地删除了方法和类,将整个游戏减少到 6 个最小 类。我在按下字符按钮('you' 按钮)时仍有延迟。有了这样一个最小的例子,我就不知道可能出了什么问题。我已将其上传到 google 驱动器,供任何人查看。
https://drive.google.com/open?id=17A-PB2517bPJc8Dek-yp2wGXsjTyTj1d
您的代码 "wrong" 没有任何内容(至少在这个问题上)。
您遇到的问题也与 FXML 的加载无关(非常慢并且您已正确处理 FX-Thread)。
出现口吃的原因如下:
- 相对
character.fxml
中的大层次结构
- 很多 CSS(删除
main.css
,您会注意到;卡顿稍微不那么突出) - 动态改变场景图(运行时 adding/removing 个节点)
每次用某个大节点替换 mainView
的 center
时,都会导致 JavaFx 运行时完全重新布局并(至少)重新设置该节点的样式。这发生在 FX-Thread 上,因此您注意到卡顿。
一种可能的缓解方法是经典的游戏开发技术:尽可能多地预分配。
只需在启动期间加载一次所有必需的 FMXL,然后将它们放入场景图中。然后在您的点击处理程序中,将您想要的节点的可见性或 (Z-) 位置更改为 show/hide。
例如,这是 StackPane
的一个很好的用例。
我稍微调整了您的代码以证明我的意思: Prototype
查看这些资源以了解更多信息: