[英]Gluon Mobile ScrollPane Optimization
我正在尝试使用嵌套的VBox实现JavaFX ScrollPane,并且在移动中滚动太快时遇到一个奇怪的问题。 问题是,如果我以较小但快速的向上手势滚动,则滚动窗格首先停滞半秒,然后继续。 随机滑动手势会变得迟钝。 无论如何,我可以对此进行优化吗?
在台式机版本上不会发生这种情况,因此我猜测这可能是我的手机和JavaFX本身的局限性。 当我为滚动窗格视口设置背景图像时,此问题更加明显。 这是一个视图的示例代码:
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")));
}
}
最终,我试图使它滚动到尽可能接近实际的本机移动环境。
我已经能够优化需要滚动的情况,以HBox作为单元格来创建自定义CharmListView,但是这样我就不能将SceneBuilder用于影响可维护性的视图。
注意:我正在使用LG G5进行测试。
这是该问题的简短视频。 滑动后即可看到。 滚动停止一秒钟,然后继续:
A.Sharma提供的答案解决了一些问题,因为它完全删除了内置ScrollPane
事件处理的基础实现。
当然,要明确解决此问题,JavaFXPorts需要一个适当的解决方案。
同时,可以通过直接在ScrollPaneSkin
解决问题来完成与现有实现更紧密配合的另一种可行方法,该方法又是负责控件事件处理的类。
该类可以在这里找到。
好消息是,可以将此类克隆到您的项目(即MyScrollPaneSkin
)中,进行修改并添加为现有ScrollPane
控件的新外观。
可能的修复
经过一些测试,问题的根源可以位于contentsToViewTimeline
动画中。 它执行1.35秒的暂停以:
阻止“余震”
然后在滚动事件处理程序中有以下行,该行检查动画是否结束,并且仅在此之后更新滚动条位置:
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。
因此,我通过使用本机滚动事件并对其进行自定义,从而破解了此修复程序。 实际上,我使用了反复试验的方法来找出使它滚动OK所需的硬值(以下因素)。
private SimpleDoubleProperty location = new SimpleDoubleProperty();
private SimpleDoubleProperty vValue = new SimpleDoubleProperty(0);
private SimpleLongProperty startTime = new SimpleLongProperty();
private SimpleLongProperty timer = new SimpleLongProperty();
private double height = MobileApplication.getInstance().getGlassPane().getHeight();
private Animation animation;
private Animation callbackAnimation;
private void scrollOverride() {
sp.addEventFilter(ScrollEvent.SCROLL, event -> {
event.consume();
});
sp.setOnTouchPressed(e -> {
if (callbackAnimation != null){
callbackAnimation.stop();
}
if (animation != null) {
animation.stop();
}
vValue.set(sp.vvalueProperty().get());
location.set(e.getTouchPoint().getY());
startTime.set((new Date()).getTime());
});
sp.setOnTouchMoved(e -> {
timer.set((new Date()).getTime());
});
sp.setOnTouchReleased(e -> {
Boolean dontOverride = false;
double deltaTime = (new Date()).getTime() - startTime.get();
double deltaY = sp.vvalueProperty().get() - vValue.get();
if(Math.abs(deltaY) < 0.05){
dontOverride = true;
}
double translation = deltaY / (deltaTime/300);
double value = 0d;
if (Math.abs(timer.get() - startTime.get()) < 500) {
value = sp.vvalueProperty().get() + translation;
} else {
value = sp.vvalueProperty().get();
dontOverride = true;
}
animation = new Timeline(
new KeyFrame(Duration.millis(deltaTime + 100),
new KeyValue(sp.vvalueProperty(), value)));
animation.play();
if (!dontOverride) {
animation.setOnFinished(evt -> {
boolean innerUp = (sp.vvalueProperty().get() - vValue.get()) < 0;
int innerSign = 1;
if (innerUp) {
innerSign = -1;
}
callbackAnimation = new Timeline(
new KeyFrame(Duration.millis(deltaTime + 150),
new KeyValue(sp.vvalueProperty(), sp.vvalueProperty().get() + (0.1 * innerSign), Interpolator.EASE_OUT)));
callbackAnimation.play();
});
}
});
}
通过执行以下操作,可以将此替代限制为仅在移动设备上发生:
if(!com.gluonhq.charm.down.Platform.isDesktop()){
scrollOverride();
}
如果有人可以对此进行优化或提出自己的解决方案,那就太好了。 同样, 我的目标主要是防止滚动时出现停顿现象。
在我的iPhone 7+上的应用程序中,这实际上运行良好。 与当我使用网络视图将浏览器注入应用程序时相比,它的工作效果相当好。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.