我正在编写一个JavaFX应用程序,它接收套接字上的数据点并实时显示它们。问题是JavaFX渲染太慢了。我有一个运行速度足够快的Swing实现,但我需要使用JavaFX。
我正在进行的约束是:
到目前为止,我的方法是使用Canvas JavaFX节点作为可视化控件,并使用接收线程来安排对Canvas的更新,以便稍后在JavaFX应用程序线程中运行,如下所示。
public void onEvent(Event event) {
....do processing...
Platform.runLater(new Runnable() {
@Override
public void run() {
graphics.setFill(...);
graphics.fillRect(...);
}});
}
我想到了一些可能加快这一步骤的方法:
sceneCanvas.getGraphicsContext2D().drawImage(processingCanvas.snapshot(SnapshotParameters(), null) 0, 0);
的代码完成的。这样做的缺点是我认为它不是线程安全的,而且快照调用似乎相对昂贵。您能否提出一些可能的方法?
谢谢!
答案 0 :(得分:3)
我认为,主要问题是您的代码将太多的绘图任务推送到FX Application线程的队列中。通常,每秒进行60次绘图操作就足够了,这相当于显示器的刷新率。如果您获得更多"传入数据"事件不是这样,你会经常抽取超过必要的东西,浪费CPU。因此,您必须将数据处理与绘画分离。
一种解决方案是使用AnimationTimer
。它的handle
方法将在每个动画帧中调用,因此通常每秒60次。动画计时器处理重新绘制,以防处理新数据。
// generic task that redraws the canvas when new data arrives
// (but not more often than 60 times per second).
public abstract class CanvasRedrawTask<T> extends AnimationTimer {
private final AtomicReference<T> data = new AtomicReference<T>(null);
private final Canvas canvas;
public CanvasRedrawTask(Canvas canvas) {
this.canvas = canvas;
}
public void requestRedraw(T dataToDraw) {
data.set(dataToDraw);
start(); // in case, not already started
}
public void handle(long now) {
// check if new data is available
T dataToDraw = data.getAndSet(null);
if (dataToDraw != null) {
redraw(canvas.getGraphicsContext2D(), dataToDraw);
}
}
protected abstract void redraw(GraphicsContext context, T data);
}
// somewhere else in your concrete canvas implementation
private final RedrawTask<MyData> task = new RedrawTask<MyData>(this) {
void redraw(GraphicsContext context, MyData data) {
// TODO: redraw canvas using context and data
}
}
// may be called by a different thread
public void onDataReceived(...) {
// process data / prepare for redraw task
// ...
// handover data to redraw task
task.requestRedraw(dataToDraw);
}