【问题标题】:javafx, dragging optimisation - various ideasjavafx、拖拽优化——各种思路
【发布时间】:2014-11-30 06:51:18
【问题描述】:

我有一个包含很多可拖动节点的应用程序,这可能会有点慢。特别是在:

  • 拖动包含其他节点的节点或
  • 在另一个 IF 中拖动节点会导致父级边界扩大 [几乎就像它正在缓存一些与范围相关的数据,对于内部的双精度值很常见,并且如果范围发生变化则需要进行额外处理](编辑:这有现在被解释为父级的布局通道)

问:对整数执行一些操作(我可以控制的少数类型)(首先转换为整数)然后重新分配给双精度数有什么好处吗? (编辑:现在已经回答了)

Q1:我还能如何加快速度?

==========更新: 感谢到目前为止的建议,我确定我的主要问题是在区域根内平移区域容器,因为它触发容器区域中的布局传递,这反过来(我认为)乒乓球在所有(大约 40) 个子节点(它们本身就很复杂 - 它们包含文本字段、按钮等)。

到目前为止,我发现最快的解决方案是在一个组中平移一个组(已经快了很多),其中组被额外修改(子类,覆盖计算...方法)以确保父/根不需要在拖动过程中调整大小(遗憾的是,不那么明显)。

class FastGroup extends Group {
    //'stop asking about size' functionality

    double widthCache;
    double heightCache;

    protected double computePrefWidth(double height) {
        return widthCache != 0 ? widthCache : super.computePrefWidth(height);
    }

    protected double computePrefHeight(double width) {
            return heightCache != 0 ? heightCache : super.computePrefHeight(width);

    }

    public void initDragging(){
    //if a child of this Group goes into drag
    //add max margins to Group's size 
        double newW = getBoundsInLocal().getWidth() + TestFXApp03.scene.getWidth()*2;
        double newH = getBoundsInLocal().getHeight() + TestFXApp03.scene.getHeight()*2;
    //cache size
        widthCache = newW;
        heightCache = newH;
    }

    public void endDragging(){
        widthCache = 0;
        heightCache = 0;
    }
}

然而,集团会制造自己的问题。

Q3:为什么我不能使用 Pane 实现相同的效果(尝试将其作为第三个选项)?根据文档,窗格:

'除了将可调整大小的子项调整为其大小之外,不执行布局 首选尺寸'

...当一个组:

'将“自动调整”其托管的可调整大小的子项到他们的首选 布局过程中的尺寸'

'不能直接调整大小'

...这对我来说听起来完全一样,但是用作根的窗格导致包含的组的平移非常缓慢。

【问题讨论】:

  • 首先,如果可能,我会尽量避免父容器的布局重新计算,这在拖动过程中代价高昂...如果拖动节点的数量太多而无法流畅和高效,您可能会转换将所有节点拖到 snapshop 图像中,拖动该图像,然后将真实节点移动到最后...
  • 整数算术或浮点数在现代处理器中并不明显快于双倍... CPU 用于其他地方...
  • @Jens-PeterHaack 有没有一种简单的方法可以将节点布局为图像时进行转换?
  • SnapshotParameters params = new SnapshotParameters(); WritableImage image = node.snapshot(params, null);

标签: java javafx


【解决方案1】:

好的...我花了一些时间检查鼠标拖动的性能与许多对象...

我是在 Mac Pro(4 核,8GB RAM)上完成的。

  1. 在 setOnMouseDragged() 处理程序中使用 setTranslateX() 和 setTranslateY() 的鼠标拖动以 17 毫秒 (60Hz) 的间隔调用。
  2. 如果我在拖动组中添加小圆圈,拖动的速度会保持在 25.000 圈。
  3. 在 100.000 圈时,拖动间隔约为 50 毫秒(并且在拖动过程中会显示一些停止)
  4. 在 1.000.000 圈时,拖动间隔约为 500 毫秒

如果我使用 snapshot() 函数将拖动实现为图像拖动,则 1.000.000 圈的快照需要 600 毫秒。然后拖起来又顺畅又快。

这里是在拖动过程中使用和不使用图像的完整代码:

public class DragMany extends Application {
    Point2D lastXY = null;
    Scene myScene;
    long lastDrag;
    ImageView dragImage; 


    void fill(Group box) {
        Color colors[] = new Color[]{Color.RED, Color.BLUE, Color.GREEN, Color.YELLOW, Color.PINK, Color.MAGENTA };
        for (int i = 0; i < 100000; i++) {
            Circle c = new Circle(2);
            box.getChildren().add(c);
            c.setFill(colors[(i/17) % colors.length]);
            c.setLayoutX(i % 30 * 3);
            c.setLayoutY((i/20) % 30*3);
        }
        box.setLayoutX(40);
        box.setLayoutY(40);

    }
    void drag1(Pane pane, Group box) {
        box.setOnMousePressed(event -> {
            lastXY = new Point2D(event.getSceneX(), event.getSceneY());
            lastDrag = System.currentTimeMillis();
        });
        box.setOnMouseDragged(event -> {
            event.setDragDetect(false);
            Node on = box;
            double dx = event.getSceneX() - lastXY.getX();
            double dy = event.getSceneY() - lastXY.getY();
            on.setLayoutX(on.getLayoutX()+dx);
            on.setLayoutY(on.getLayoutY()+dy);
            lastXY = new Point2D(event.getSceneX(), event.getSceneY());
            event.consume();
        });

        box.setOnMouseReleased(d ->  lastXY = null);
    }

    void drag3(Pane pane, Group box) {
        box.setOnMousePressed(event -> {
            long now = System.currentTimeMillis();

            lastXY = new Point2D(event.getSceneX(), event.getSceneY());
            SnapshotParameters params = new SnapshotParameters();
            params.setFill(Color.TRANSPARENT);
            WritableImage image = box.snapshot(params, null);
            dragImage = new ImageView(image);
            dragImage.setLayoutX(box.getLayoutX());
            dragImage.setLayoutY(box.getLayoutY());
            dragImage.setTranslateX(box.getTranslateX());
            dragImage.setTranslateY(box.getTranslateY());
            pane.getChildren().add(dragImage);
            dragImage.setOpacity(0.5);
            box.setVisible(false);
            System.out.println("Snap "+(System.currentTimeMillis()-now)+"ms");

            pane.setOnMouseDragged(e -> {
                if (dragImage == null) return;
                Node on = dragImage;
                double dx = e.getSceneX() - lastXY.getX();
                double dy = e.getSceneY() - lastXY.getY();
                on.setTranslateX(on.getTranslateX()+dx);
                on.setTranslateY(on.getTranslateY()+dy);
                lastXY = new Point2D(e.getSceneX(), e.getSceneY());
                e.consume();
            });
            pane.setOnMouseReleased(e -> {
                if (dragImage != null) {
                    box.setTranslateX(dragImage.getTranslateX());
                    box.setTranslateY(dragImage.getTranslateY());
                    pane.getChildren().remove(dragImage);
                    box.setVisible(true);
                    dragImage = null;
                }
                lastXY = null;
                e.consume();
            });

            lastDrag = System.currentTimeMillis();
            event.consume();
        });
    }

    public void start(Stage primaryStage) {
        Pane mainPane = new Pane(); 
        myScene = new Scene(mainPane, 500, 500);
        primaryStage.setScene(myScene);
        primaryStage.show();

        Group all = new Group();
        fill(all);
        mainPane.getChildren().add(all);
        drag3(mainPane, all);
    }

    public static void main(String[] args) {
        launch(args);
    }

【讨论】:

  • 使用文本对象代替圆圈也相当快......它比相同数量的圆圈慢 2-3 倍。在拖动和生成快照中。 (1600 毫秒)
  • 因此,在普通 javafx 中,许多对象的普通拖动非常快,也无需使用快照。如果在拖动过程中触发布局计算,它会变慢。
  • 也很有趣:如果我使用 1000000 个对象和图像拖动,如果我在(不可见的)对象组上,图像拖动会变慢。 Javafx 似乎在这里遍历了不可见的对象...
  • 也许调用该组的 setMouseTransparent(true) 将有助于防止不可见的物体减慢速度? (尽管根据 MouseEvent 文档,拖动事件仅发送到“抓取”时位于鼠标下方的节点,除非调用 startFullDrag()。)
  • @DailyFrankPeter: 速度设置MouseTransparent(true)没有真正的区别,如果通过隐形组还是慢很多。
【解决方案2】:

使用整数或浮点数而不是双精度数不会给您带来太多性能提升。

相反,您应该在更改节点位置时尝试优化。例如,当你拖动时,你可以只渲染所有被拖动的节点,而不是改变所有节点的位置,并且只改变一个节点的位置。完成拖动后,您可以重新计算拖动的所有节点的位置。

这是我对你现在如何做的猜测:

void drag(float x, float y) {
    setPosition(x, y); // I'm gueessing this is where your bottle neck is 
    for (Node node : nodes) {
        node.drag(x, y);
    }
}

void render() {
    screen.draw(this.x, this.y);
    for (Node node : nodes) {
        node.render();
    }
}

下面是我将如何通过引入渲染偏移来优化它,以便您只在完成拖动时设置所有节点的位置:

float xo, yo; // render offsets

void drag(float x, float y) {
    xo = x;
    yo = y;
}

void dragEnd(float x, float y) {
    setPosition(x, y);
    for (Node node : nodes) {
        node.dragEnd(x, y);
    }
}

void render(float xo, float yo) {
    xo += this.xo;
    yo += this.yo;

    screen.draw(this.x + xo, this.y + yo); // render with at offset
    for (Node node : nodes) {
        node.render(xo, yo);
    }
}

【讨论】:

  • 我听到你在说什么,但不确定我是否理解这段代码。但是,如果您本质上是说我应该先完成其他内容的按下-拖动-释放,然后将记录的位置更改应用于所有节点,那么您能否澄清一下您在拖动时建议显示哪些视觉反馈?
猜你喜欢
  • 2013-01-14
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-01-10
相关资源
最近更新 更多