我以前在 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);
}
}
});
}