【问题标题】:JavaFX Preloader ThreadJavaFX 预加载器线程
【发布时间】:2017-04-21 20:36:48
【问题描述】:

我需要有人解释一下 Preloader 在 JavaFX 应用程序中的工作原理。我已经找到了生命周期,我知道首先调用 preloader 的 start(..) 然后调用 Application 的 start(..) 但这两个方法是从同一个线程调用的吗?我试图在应用程序的启动方法中制作一个简单的应用程序,将 Thread.sleep(8000) 放在预加载器的开头,我创建了一个简单的阶段并展示它。但是当我运行应用程序时,它只是在黑色阶段冻结,并且仅在 8 秒后正确显示预加载阶段,但为什么呢?

用源代码更新我的帖子:

公共类 PreloaderApp 扩展 Preloader {

public void init(){
    System.out.println("Thread Preloader ID:"+ Thread.currentThread().getId());
    System.out.println("  ------  ");

    Pane effectRegion = LoaderFrame.getInstance().getEffectPane();
    JFXFillTransition fill = new JFXFillTransition();
    fill.setDuration(Duration.millis(400));
    fill.setRegion(effectRegion);
    fill.setAutoReverse(true);
    fill.setCycleCount(Animation.INDEFINITE);
    fill.setFromValue(Color.WHITE);
    fill.setToValue(Color.rgb(0,77,147));

    fill.play();

}

@Override
public void start(Stage primaryStage){
    System.out.println("Thread Preloader(start) ID:"+ Thread.currentThread().getId());
    System.out.println("  ------  ");
    LoaderFrame.getInstance().show();

}

}

public class LoaderFrame extends Stage {

私有静态最终 LoaderFrame 实例 = new LoaderFrame();

public static LoaderFrame getInstance(){
    return instance;
}


private Scene scene;
private AnchorPane root;
private BorderPane wraper;
private StackPane effectPane;

private JFXButton loaderPathButton;


public LoaderFrame(){
    initScene();
    this.initStyle(StageStyle.TRANSPARENT);
    this.getIcons().add(new Image("file:imgs/favico/icon48.png"));
}

public void initScene(){
    FXMLLoader loader = new FXMLLoader(Main.class.getResource("xml/loader_frame.fxml"));
    root = null;

    try {
        root = loader.load();
        wraper = (BorderPane) root.lookup("#rootPane");
        loaderPathButton = (JFXButton) root.lookup("#loaderPathButton");
        effectPane = (StackPane) root.lookup("#effectPane");
        scene = new Scene(root);
        scene.setFill(Color.TRANSPARENT);
        scene.getStylesheets().add("file:css/loader.css");
        this.setScene(scene);
    } catch (IOException e) {
        e.printStackTrace();
    }

}


public Pane getEffectPane(){
    return effectPane;
}

}

【问题讨论】:

    标签: javafx preloader


    【解决方案1】:

    要调查 Preloader 生命周期,您可以将以下行添加到您的 Preloader 类:

        @Override
        public void handleStateChangeNotification(StateChangeNotification info) {
            System.out.println("state: " + info.getType());
        }
    

    Application 类中,您可以在init / start 方法中添加System.out.prinln 消息。这将显示 Preloader 运行其不同的状态:BEFORE_LOADBEFORE_INITBEFORE_START

    由于Preloaders start methodJavaFXThread 上被调用,您必须将对Thread.sleep 的调用封装在Platform.runlater 中。

    请注意,预加载器遵循与其他 JavaFX 应用程序相同的规则,包括 FX 线程规则。特别是,类构造函数和 init() 方法将在非 FX 线程上调用,而 start() 将在 FX 应用程序线程上执行。这也意味着应用程序构造函数/init() 将与预加载器 start() 同时运行。

    如果您需要进行一些耗时的初始化,您应该在您的应用程序init 方法中进行。当Preloader state 更改为BEFORE_START 时,您可以隐藏您的Preloader stage

    public class App extends Application {
    
        private static final int PRELOADER_SHOWTIME_MILLIS = 2000;
    
        @Override
        public void init() throws Exception {
            long start = System.currentTimeMillis();
    
            // time consuming initializations
    
            long duration = System.currentTimeMillis() - start;
            long remainingShowTime = PRELOADER_SHOWTIME_MILLIS - duration;
    
            if(remaingShowTime > 0 ){
                Thread.sleep(remainingShowTime);
            }
        }
    
        @Override
        public void start(Stage primaryStage) {
            StackPane root = new StackPane(new Label("application"));
            Scene scene = new Scene(root, 400, 400);
    
            primaryStage.setScene(scene);
            primaryStage.show();
        }
    
        public static void main(String[] args) {
            launch(args);
        }
    }
    
    public class TestPreloader extends Preloader {
    
        private Stage stage;  
    
        @Override
        public void start(Stage primaryStage) throws Exception {
            stage = primaryStage;
    
            Rectangle rect = new Rectangle(200, 200, Color.RED);
            FadeTransition transition = new FadeTransition(Duration.seconds(2), rect);
            transition.setFromValue(0);
            transition.setToValue(1);
    
            Label lbl = new Label("preloader");
            StackPane root = new StackPane(rect, lbl);
    
            primaryStage.setScene(new Scene(root));
            primaryStage.show();
    
            transition.play();
        }
    
        @Override
        public void handleStateChangeNotification(StateChangeNotification info) {
            if (info.getType() == Type.BEFORE_START) {
               stage.hide();
            }
         }
    
    }
    
    public class Main {
    
        public static void main(String[] args) {
            LauncherImpl.launchApplication(App.class, TestPreloader.class, args);
        }
    
    }
    

    【讨论】:

    • 好吧,我发现preloader和Application是由同一个线程执行的。但是假设我有一些动画,比如预加载器阶段的 javafx 转换(启动方法),我如何才能保持活动状态或使用另一个线程执行该转换?
    • 我想您想在特定时间内显示带有动画的启动画面。如果是这种情况,您可以在Application.init 中测量花费的时间,并在init 中调用Thread.sleep(remainingShowTime) 作为最后一条语句
    • 澄清一下:您的应用程序start() 正在JavaFXThread 上运行,就像Preloader start() 一样。 Application init() 在非 Fx 线程上运行,因此它与 Preloader start() 并发运行
    • 好的,首先我要感谢 jns 的重播,但是例如我有一个作为 Singleton 的预加载器阶段(带有私有构造函数的静态实例),如果我尝试调用 MyPreloaderStage.getInstance在 Preloader 的 init 中它给了我一个 RuntimeException(( 我只想在 Preloader 的阶段保持一个过渡动画并加载一个主要的(应用程序阶段)也许这是不可能的..
    • 能不能把涉及到的类的完整代码贴出来,不然真的不好说什么不工作。
    猜你喜欢
    • 2016-02-14
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多