美文网首页
JavaFx 之 Preloader

JavaFx 之 Preloader

作者: Ggx的代码之旅 | 来源:发表于2018-05-16 21:53 被阅读358次

    做过Java的同学都用过Eclipse或者IDEA,这两款编辑器在启动的时候都会有一个预加载的画面,有一个专业的术语形容它——SplashScreen。当然在JavaFx中也提供了这种机制,实现这样的东西我们需要用到一个类Preloader这里称之为预加载器。

    Preloader

    预加载器是在主应用程序之前启动可以定制启动体验的小应用程序。
    预加载有以下几个关键:

    1. 可以获取加载主应用程序资源的进度通知消息
    2. 可以获取错误通知消息
    3. 可以获取应用程序初始化和启动的通知
    4. 决定主应用程序是否可见

    默认情况下预加载器显示在应用程序的顶部,在预加载程序可见之前,它是不可见的。预加载器需要隐藏自己以使应用程序可见。好的做法是不要比application.start()被调用之前调用,否则应用程序本身就不可见。

    预加载器还可以与主应用程序协作以实现高级视觉效果或共享数据(例如实现登录屏幕)。预加载器获得对应用程序的引用,并且如果应用程序实现预加载器知道并依赖的接口,则可以从应用程序中提取所需的用于合作的数据。通常不推荐以应用程序会直接调用它们的方式设计预加载器,因为如果应用程序预加载器不存在或者主应用程序已签名,这将导致用户体验不佳。

    如果主应用程序没有指定预加载器,就会使用默认的预加载器,此预加载器可以被用户定制。

    自定义预加载器的实现应遵循以下规则:

    1. 设计一个类并继承自Preloader
    2. 预加载器所需的类需要打包在单独的jar中。我们建议这个jar是不签名的。
    3. JNLP部署描述符应该具有预载入类属性,在JavaFX-DESC元素中以类的全名作为值,并且需要进度的JAR需要download=“progress”类型

    应用程序还可以使用notifyPreloader方法向预加载器发送自定义通知。这样,预加载器也可以显示应用程序初始化进度。

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

    预加载器通知的回调将在FX应用程序线程上传递。

    Preload详解

    查看Preload源码可以发现,Preload其实本质上仍然是一个Application,因为它就继承自Application。区别在于在里面定义个一些列的通知类型和回调方法。下面分别介绍它的通知方法:

    1. void handleProgressNotification(ProgressNotification info)
      此方法是一个进度指示通知方法。该方法在JavaFx运行应用程序资源被载入时调用用来指示进度。通过ProgressNotification中的getProgress()方法获取进度,事实上此类中也就这一个方法可用。这个方法不会被notifyPreloader调用传递。

    2. void handleStateChangeNotification(StateChangeNotification info)
      此方法当应用程序状态改变是通知。这个方法由FX运行时作为应用程序生命周期的一部分调用。

    3. void handleApplicationNotification(PreloaderNotification info)
      此方法是应用程序生成的通知。这个方法由FX运行时调用,以发送通过notifyPreloader发送的通知。应用程序不应该直接调用这个方法,而是应该使用notifyPreloader来避免代码的混乱。

    发送预加载器通知notifyPreloader

    在主应用程序中Application下有一个notifyPreloader方法,和预加载器之间的交互主要通过它来实现。正如上面所说一样这个方法只会传递Preloader中的handleApplicationNotification()方法

    Example

    下面有一个示例,用来演示启动应用过程中预加载耗时资源的例子:

    /**
     * 这是预加载器界面
     */
    public class TestLongInitAppPreloader extends Preloader {
        private Stage stage;
        private ProgressBar bar;
    
        @Override
        public void start(Stage primaryStage) throws Exception {
            this.stage=primaryStage;
            //这里可以看到和Application一样,也有舞台,我们可以定制自己的界面
            BorderPane p = new BorderPane();
            ImageView iv=new ImageView();
            iv.setImage(new Image(ClassLoader.getSystemResourceAsStream("images/d.jpg")));
            p.setCenter(iv);
            bar = new ProgressBar(0);
            p.setBottom(bar);
            primaryStage.initStyle(StageStyle.UNDECORATED);
            primaryStage.setScene(new Scene(p));
            primaryStage.show();
        }
    
        @Override
        public void handleProgressNotification(ProgressNotification info) {
            System.out.println("handleProgressNotification="+info.getProgress());
            if (info.getProgress() != 1.0) {
                bar.setProgress(info.getProgress() / 2);
            }
        }
    
    
        /**
         * 重载这个方法可以处理应用通知
         * @param info
         */
        @Override
        public void handleApplicationNotification(PreloaderNotification info) {
            if (info instanceof ProgressNotification) {
                //提取应用程序发送过来的进度值
                double v = ((ProgressNotification) info).getProgress();
                System.out.println("handleApplicationNotification="+v);
                bar.setProgress(v);
            } else if (info instanceof StateChangeNotification) {
                //隐藏/或者关闭preloader
    //            stage.hide();
                stage.close();
            }
        }
    
    }
    
    /**
     * 主应用程序
     */
    public class TestLongInitApp extends Application {
    
        BooleanProperty ready=new SimpleBooleanProperty(false);
    
        @Override
        public void start(Stage primaryStage) throws Exception {
            //这里可能处理一些耗时操作
            new Thread(longTask()).start();
    
            primaryStage.setTitle("主界面");
            primaryStage.setScene(new Scene(new Label("Main Application started"),
                    400, 400));
            primaryStage.setMaximized(true);
    
            ready.addListener((observable, oldValue, newValue) -> {
                if(Boolean.TRUE.equals(newValue)){
                    Platform.runLater(primaryStage::show);
                }
            });
        }
    
        private Task longTask(){
            return new Task<Void>() {
                @Override
                protected Void call() throws Exception {
                    //模拟准备耗时数据
                    int max = 10;
                    for (int i = 1; i <= max; i++) {
                        Thread.sleep(1000);
                        // 发送进程给预加载器主要通过Application中的notifyPreloader(PreloaderNotification)方法
                        Preloader.ProgressNotification notification=new Preloader.ProgressNotification(((double) i)/max);
                        notifyPreloader(notification);
                    }
                    // 这里数据已经准备好了
                    // 在隐藏预加载程序之前防止应用程序过早退出
                    ready.setValue(Boolean.TRUE);
    
                    notifyPreloader(new Preloader.StateChangeNotification(
                            Preloader.StateChangeNotification.Type.BEFORE_START));
                    return null;
                }
            };
        }
    }
    

    欢迎共同探讨更多安卓,java,c/c++相关技术QQ群:392154157

    相关文章

      网友评论

          本文标题:JavaFx 之 Preloader

          本文链接:https://www.haomeiwen.com/subject/kwbmdftx.html