2014-02-28 25 views
0

当我的自定义控件调整大小时,我需要特定的行为。当调整控件大小(resize或resizeRelocate调用的方法)时,首先我只需要缩放子节点以适应新的边界。如果在1秒内没有尺寸变化 - 进行昂贵的计算和重新布置子节点。如果我重新计算每次调整大小调用 - 它会使Stage调整大小非常缓慢。我怎样才能做到这一点?javafx自定义控件的延迟更新

这是例子,这里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

尝试重构你的问题,添加一些片段,并使其简单!我无法理解你的问题! – ItachiUchiha

+0

@IchichiUchiha,用示例代码更新。我需要重新计算形状而不是缩放以保持strokeWidth的固定。 –

回答

0

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

在我的情况下,我在搜索文本框中使用了它,实际搜索只在用户未按下某个键一段时间(比如500ms)后触发,阻止触发搜索每个关键笔划。

在你的情况下,你可以在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,而不是使用Task。这将在未来的某个时间执行可运行的程序,其优点是以这种方式运行的所有可运行程序都按先进先出顺序处理。这可以方便地防止事情失序,同时仍然可以在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); 
      } 
     } 
    }); 
} 
+0

谢谢!我会很快尝试你的解决方案! –

+0

没有probs,希望它适合你。我刚刚添加了一个使用Platform.runLater()的替代实现,它也应该可以工作。 – Gumbatron