2014-02-28 51 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