【问题标题】:Delayed update of javafx custom controljavafx自定义控件延迟更新
【发布时间】:2014-02-28 08:53:14
【问题描述】:

当我的自定义控件调整大小时,我需要特定的行为。当控件调整大小(调用调整大小或 resizeRelocate 方法)时,我只想缩放子节点以适应新的边界。如果在 1 秒内没有大小变化 - 进行昂贵的计算和子节点的重新布局。如果我在每次调整大小调用时重新计算 - 它会使舞台调整大小变得非常滞后。我怎样才能做到这一点?

这是示例,这里CurvePlot 只是数据模型:

class ShapeCurvePlot extends Polyline {
    private final CurvePlot model;

    public ShapeCurvePlot(CurvePlot model) {
        Objects.requireNonNull(model);
        this.model = model;
        strokeProperty().bind(model.strokeProperty());
        strokeWidthProperty().bind(model.strokeWidthProperty());
    }

    @Override
    public boolean isResizable() {
        return true;
    }

    @Override
    public void resize(double width, double height) {
        // "expensive calculation"
        Series series = model.getSeries();
        double xOffset = series.valueAxis().getLeft();
        double yOffset = series.keyAxis().getLeft();
        double yScale = height / series.keyAxis().range();
        double xScale = width / series.valueAxis().range();

        getPoints().clear();
        for (Map.Entry<Double, Double> item : series.data().entrySet()) {
            double x = item.getValue();
            double y = item.getKey();
            getPoints().addAll((x - xOffset) * xScale, (y - yOffset) * yScale);
        }
    }
}

虽然在此示例中调整大小的速度很快,但我需要的其他形状并不那么容易重新计算。我要求通用解决方案来延迟计算,直到 X 秒内没有调整大小。

ps。对不起我的英语,我不是母语人士..

【问题讨论】:

  • 尝试重新构建您的问题,添加一些 sn-ps 并使其变得简单!我无法理解你的问题!
  • @ItachiUchiha,使用示例代码进行了更新。我需要重新计算形状而不是缩放以保持 strokeWidth 固定。

标签: java javafx javafx-8 java-8


【解决方案1】:

我以前在 C# 和 Java 中都遇到过这个问题。我的解决方案,并不是说它是最好的解决方案,但它似乎有效,是使用某种形式的计时器来触发昂贵的重新计算。

在我的情况下,我在搜索文本框中使用了它,实际搜索仅在用户一段时间没有按键(比如 500 毫秒)后才会触发,从而阻止了每次按键触发的搜索。

在您的情况下,您可以在 resize() 方法中触发计时器。当计时器用完时,它会执行昂贵的操作。在已经等待的情况下触发计时器会导致计时器被重置。

在 java 中,我使用 java.util.Timer 作为计时器,然后运行我创建的 java.util.TimerTask。在 TimerTask 的 run() 方法中,我创建了一个 javafx.concurrent.Task 并在一个新线程中运行它。 Task 的 call() 方法是完成工作的地方。这确保了工作是在 JavaFX 线程上完成的,否则您会遇到线程问题。

希望对你有所帮助。

编辑...这里有一些代码可以完成我上面所说的。 注意:这段代码完全未经测试,我认为它应该可以工作

class ShapeCurvePlot extends Polyline {
private class ResizeTimerTask extends TimerTask {
    private ShapeCurvePlot plot;

    public ResizeTimerTask(ShapeCurvePlot plot) {
        this.plot = plot;
    }

    /* (non-Javadoc)
     * @see java.util.TimerTask#run()
     */
    @Override
    public void run() {
        Task<Object> t = new Task<Object>() {
            @Override
            protected Object call() throws Exception {
                // "expensive calculation"
                Series series = plot.getSeries();
                double xOffset = series.valueAxis().getLeft();
                double yOffset = series.keyAxis().getLeft();
                double yScale = height / series.keyAxis().range();
                double xScale = width / series.valueAxis().range();

                plot.getPoints().clear();
                for (Map.Entry<Double, Double> item : series.data().entrySet()) {
                    double x = item.getValue();
                    double y = item.getKey();
                    plot.getPoints().addAll((x - xOffset) * xScale, (y - yOffset) * yScale);
                }
            }
        }

        new Thread(t).start();
    }
}

private static int RESIZE_DELAY_MS = 1000;
private final CurvePlot model;
private Timer resizeTimer;

public ShapeCurvePlot(CurvePlot model) {
    Objects.requireNonNull(model);
    this.model = model;
    strokeProperty().bind(model.strokeProperty());
    strokeWidthProperty().bind(model.strokeWidthProperty());
}

@Override
public boolean isResizable() {
    return true;
}

public Series getSeries() {
    return this.model.getSeries();
}

@Override
public void resize(double width, double height) {
    // cancel the current task (if any).
    if(this.resizeTimer != null) {
        this.resizeTimer.cancel();
        this.resizeTimer.purge();
        this.resizeTimer = null;
    }

    try {
        // create a new task that will be executed in 1 second.
        this.resizeTimer = new Timer();
        this.resizeTimer.schedule(new ResizeTimerTask(this), RESIZE_DELAY_MS);
    } catch (OutOfMemoryError oom) {
        oom.printStackTrace();
        if(this.resizeTimer != null) {
            this.resizeTimer.cancel();
            this.resizeTimer.purge();
            this.resizeTimer = null;
        }
    }
}

}

进一步编辑...今天刚刚花了一些时间处理 JavaFX 线程问题,还有另一个选项可用于在 JavaFX 显示线程中运行耗时的任务。您可以使用 javafx.application.Platform.runLater(Runnable r) 在 JavaFX 线程中执行 Runnable,而不是使用任务。这将在未来某个时间执行 runnable,其好处是所有以这种方式运行的 runnable 都按 FIFO 顺序处理。这可以很方便地防止事情发生混乱,同时仍然能够在 JavaFX 显示线程上以异步方式运行它们。

为此,您需要将 ResizeTimerTask 类的 run() 方法的实现更改为:

public void run() {
    Platform.runLater(new Runnable() {

        @Override
        public void run() {
                // "expensive calculation"
            Series series = plot.getSeries();
            double xOffset = series.valueAxis().getLeft();
            double yOffset = series.keyAxis().getLeft();
            double yScale = height / series.keyAxis().range();
            double xScale = width / series.valueAxis().range();

            plot.getPoints().clear();
            for (Map.Entry<Double, Double> item : series.data().entrySet()) {
                double x = item.getValue();
                double y = item.getKey();
                plot.getPoints().addAll((x - xOffset) * xScale, (y - yOffset) * yScale);
            }
        }
    });
}

【讨论】:

  • 没有问题,希望它对你有用。我刚刚使用 Platform.runLater() 添加了一个替代实现,它也应该可以工作。
猜你喜欢
  • 1970-01-01
  • 2011-01-17
  • 1970-01-01
  • 1970-01-01
  • 2018-08-05
  • 1970-01-01
  • 1970-01-01
  • 2011-12-22
  • 2014-04-28
相关资源
最近更新 更多