我的自定义控件调整大小时需要特定的行为。当控件调整大小(调整resize或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。对不起我的英语,我不是母语人士..
答案 0 :(得分:0)
我之前遇到过这个问题,包括C#和Java。我的解决方案,并不是说它是最好的解决方案,但似乎有用,就是使用某种形式的计时器来触发昂贵的重新计算。
在我的情况下,我在搜索文本框中使用了它,实际搜索仅在用户未按键一段时间(比如500ms)后触发,从而阻止搜索每个键击的搜索。 / p>
在您的情况下,您可以在resize()方法中触发计时器。当计时器用完时,它会执行昂贵的操作。在定时器等待时触发定时器会导致定时器复位。
在java中,我通过使用java.util.Timer为计时器执行此操作,然后运行我创建的java.util.TimerTask。在TimerTask的run()方法中,我创建了一个javafx.concurrent.Task并在一个新的Thread中运行它。 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,而不是使用Task。这将在将来的某个时间执行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);
}
}
});
}