目标是从各种SceneGraph组件生成报告,同时向用户显示一些ProgressIndicator。
我们需要
由于除非从FxThread无法更新GUI,否则问题将变为:
如何显示长时间运行的GUI操作的进度指示器?
我的第一种方法是简单地将逻辑放入Task显然失败,导致执行snapshot method时出现java.lang.IllegalStateException: Not on FX application
。
它包含一个Fx thread check,我认为这意味着暂时不能从场景中删除节点。
使用Platform.runLater也不够用,因为它只能用于写入数据到GUI,但不能用于读取。 (或者可以吗?)
在短时间内,我使用SwingWorker进行了评估,但发现与任务相比,它没有任何好处。
即使我们忽略读/写问题一秒钟。考虑这个例子:
package com.isp.lpt.progress;
import java.util.Random;
import java.util.concurrent.Executors;
import java.util.stream.IntStream;
import java.util.stream.Stream;
import javafx.application.Application;
import javafx.application.Platform;
import javafx.beans.property.LongProperty;
import javafx.beans.property.SimpleLongProperty;
import javafx.concurrent.Task;
import javafx.scene.Group;
import javafx.scene.Scene;
import javafx.scene.control.ProgressIndicator;
import javafx.scene.effect.DropShadow;
import javafx.scene.paint.Color;
import javafx.scene.shape.Circle;
import javafx.stage.Stage;
public class GuiTaskExample extends Application
{
private static final Random RANDOM = new Random();
private final static int WIDTH = 100;
private final static int HEIGHT = 100;
@Override
public void start( final Stage primaryStage )
{
final Group root = new Group();
final Group circleGroup = new Group();
final ProgressIndicator indicator = new ProgressIndicator();
root.getChildren().add( circleGroup );
root.getChildren().add( indicator );
final Scene scene = new Scene( root );
final GuiTaskExample that = this;
final Task<Void> task = new Task<Void>()
{
@Override
protected Void call() throws Exception
{
try (final IntStream intStream = RANDOM.ints( 1, HEIGHT ))
{
final long maxSize = HEIGHT * WIDTH;
final Stream<Circle> circles = intStream.mapToObj( that::createCircleThing ).limit( maxSize );
//local variables must be final for lambdas
final LongProperty counter = new SimpleLongProperty( 1 );
circles.forEach( circle ->
{
updateProgress( counter.get(), maxSize );
counter.set( counter.get() + 1 );
Platform.runLater( ( ) -> circleGroup.getChildren().add( circle ) );
} );
return null;
}
}
};
indicator.progressProperty().bind( task.progressProperty() );
task.setOnSucceeded( done ->
{
root.getChildren().remove( indicator );
primaryStage.sizeToScene();
} );
task.exceptionProperty().addListener( ( observable, oldValue, newValue ) -> newValue.printStackTrace() );
Executors.newSingleThreadExecutor().execute( task );
primaryStage.setScene( scene );
primaryStage.show();
}
private Circle createCircleThing( final int radius )
{
final Circle circle = new Circle();
System.out.println( radius );
circle.setRadius( RANDOM.nextInt( radius ) );
circle.setCenterX( circle.getRadius() + RANDOM.nextInt( WIDTH ) );
circle.setCenterY( circle.getRadius() + RANDOM.nextInt( HEIGHT ) );
circle.setStrokeWidth( RANDOM.nextDouble() * 4 );
circle.setStroke( randomPaint() );
circle.setFill( randomPaint() );
circle.setEffect( new DropShadow( RANDOM.nextInt( 12 ), randomPaint() ) );
return circle;
}
private Color randomPaint()
{
return Color.rgb( RANDOM.nextInt( 255 ), RANDOM.nextInt( 255 ), RANDOM.nextInt( 255 ),
RANDOM.nextDouble() );
}
public static void main( final String[] args )
{
launch( args );
}
}
该示例在任务中创建了大量GUI元素,然后将其绘制到屏幕上,但(至少在我的机器上)绘制到屏幕上的时间最长,我想隐藏在进度指示器后面
所以问题变成了: 如何在JavaFx中绘制到屏幕外缓冲区?
对于在Fx中长时间运行GUI操作和/或使用快照的特定用例的一般情况,我愿意接受建议。