繁体   English   中英

Gluon Mobile ScrollPane优化

[英]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/resourcesjava.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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM