我正在尝试使用嵌套的 VBox 实现 JavaFX ScrollPane,并且在移动设备中滚动太快时遇到一个奇怪的问题。问题是,如果我以较小但快速的向上手势滚动,滚动 Pane 首先会停止半秒,然后继续。它在随机滑动手势中变得滞后。我可以优化这个吗?
这不会发生在桌面版本上,所以我猜测这可能是我的手机和 JavaFX 本身的限制。 当我为滚动 Pane 视口(viewport)设置背景图像时,此问题更加明显。以下是 View 的一些示例代码:
public class StackUserView extends View {
private Label label;
public StackUserView(String name) {
super(name);
initDisplay();
}
private void initDisplay() {
this.label = new Label("10");
label.setFont(Font.font(40d));
VBox box = new VBox();
box.setSpacing(10);
box.setAlignment(Pos.CENTER_RIGHT);
for(int i = 0; i < 100; i++){
Label label = new Label("HELLO");
label.setCache(true);
label.setCacheHint(CacheHint.SPEED);
label.setCacheShape(true);
box.getChildren().add(label);
}
ScrollPane pane = new ScrollPane(box);
pane.setCacheHint(CacheHint.QUALITY);
pane.setFitToHeight(true);
pane.setFitToHeight(true);
this.setCenter(pane);
}
@Override
protected void updateAppBar(AppBar appBar) {
appBar.setNavIcon(MaterialDesignIcon.MENU.button(e -> MobileApplication.getInstance().showLayer(SpeedSelect.MENU_LAYER)));
appBar.setTitleText(this.getName());
appBar.getActionItems().add(MaterialDesignIcon.SEARCH.button(e -> System.out.println("Search")));
}
}
最终,我试图让它滚动尽可能接近实际的 native 移动环境。
我已经能够优化需要滚动的情况,创建一个使用 HBox 作为单元格的自定义 CharmListView,但是我无法将 SceneBuilder 用于我的 View ,这会影响可维护性。
注意:我使用 LG G5 进行测试。
这是有关该问题的简短视频。刷卡后就可以看到了。滚动停止一秒钟然后继续:
最佳答案
A.Sharma 提供的答案解决了一些问题,因为它完全删除了内置 ScrollPane
事件处理的底层实现。
当然,要彻底解决这个问题,需要在JavaFXPorts中找到合适的解决方案。
同时,另一种可能的方法可以通过直接在 ScrollPaneSkin
中解决问题来与现有实现更紧密地配合,而 ScrollPaneSkin 又是负责事件处理的类。控制。
可以找到该类 here .
好消息是,此类可以克隆到您的项目(即 MyScrollPaneSkin
)、修改并添加为现有 ScrollPane 的新外观
控制。
可能的修复
经过一些测试,问题的根源可以定位在 contentsToViewTimeline
动画中。它执行 1.35 秒的暂停来:
block out 'aftershocks'
然后,滚动事件处理程序中有这一行,用于检查动画是否结束并仅在之后更新滚动条位置:
if (!(((ScrollEvent)event).isInertia()) || (((ScrollEvent)event).isInertia()) &&
(contentsToViewTimeline == null ||
contentsToViewTimeline.getStatus() == Status.STOPPED)) {
vsb.setValue(newValue);
...
}
虽然不应删除此动画,但即使动画处于空闲状态,滚动条也应更新。
我进行了以下更改,将第 517-527 行替换为:
/*
** if there is a repositioning in progress then we only
** set the value for 'real' events
*/
if ((newValue <= vsb.getMax() && newValue >= vsb.getMin())) {
vsb.setValue(newValue);
}
boolean stop = ! ((ScrollEvent) event).isInertia() || ((ScrollEvent) event).isInertia() && (contentsToViewTimeline == null || contentsToViewTimeline.getStatus() == Status.STOPPED);
if ((newValue > vsb.getMax() || newValue < vsb.getMin()) && (!mouseDown && !touchDetected) && stop) {
startContentsToViewport();
}
if (stop) {
event.consume();
}
在您的项目中,现在替换内置皮肤:
ScrollPane pane = new ScrollPane(box);
pane.setSkin(new MyScrollPaneSkin(pane));
我已经在 Android 和 iOS 上对其进行了测试,在这两种情况下,动画都很流畅,并且没有任何问题的痕迹。
此外,这些属性可能会有所帮助(使用添加到 /src/main/resources
的 java.custom.properties
):
gluon.experimental.performance=true
com.sun.javafx.gestures.scroll.inertia.velocity=2000
如果可行,可以将其提交到 JavaFXPorts 问题或作为 PR。
关于Gluon 移动滚动 Pane 优化,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/48489652/