zoukankan      html  css  js  c++  java
  • JavaFX桌面应用-loading界面

    上次使用JavaFX开发了一个视频转码工具,当用户点击“启动”按钮开始转码的时候,会禁用启动按钮,防止多次启动转码。
    这种处理方式对用户来说可能并是很友好,其实可以在启动转码的时弹出一个loading界面,告诉用户正在进行视频转码。

    ~ JavaFX桌面应用开发系列文章传送门 ~

    1. JavaFX桌面应用开发-HelloWorld
    2. JavaFX布局神器-SceneBuilder
    3. JavaFX让UI更美观-CSS样式
    4. JavaFX桌面应用-为什么应用老是“未响应”
    5. JavaFX桌面应用-MVC模式开发,“真香”
    6. JavaFX桌面应用-loading界面 (本文)
    7. JavaFX桌面应用-表格用法
    8. JavaFX桌面应用-视频转码工具

    重新改造一下之前的转码程序,使用loading界面提示用户视频正在转码,如下图:

    针对这种通用的loading界面,可以使用JavaFX的stage开发一个通用的组件。

    这里需要注意的是:

    1. loading界面没有边框的
    2. loading界面背景是透明的
    3. loading附着于主Stage

    针对以上三点,可以分别设置loading stage的样式及模式:

    // 设置stage无任何装饰
    stage.initStyle(StageStyle.UNDECORATED);
    // 设置stage背景透明
    stage.initStyle(StageStyle.TRANSPARENT);
    // 设置stage的模式
    stage.initModality(Modality.APPLICATION_MODAL);
    

    loading界面由两部分组成,分别是loading动画(ProgressIndicator)和提示信息(Label),如下图:

    所以可以采用VBox来布局(这里直接采用Java代码布局,不采用fxml):

    // message
    Label adLbl = new Label(ad);
    adLbl.setTextFill(Color.BLUE);
    
    // progress
    ProgressIndicator indicator = new ProgressIndicator();
    indicator.setProgress(-1);
    indicator.progressProperty().bind(work.progressProperty());
    
    // pack
    VBox vBox = new VBox();
    vBox.setSpacing(10);
    vBox.setBackground(Background.EMPTY);
    vBox.getChildren().addAll(indicator, adLbl);
    

    对于loading界面的宽度可以通过信息来计算,而loading界面的位置则设置为主stage的中心。

    stage.setWidth(ad.length() * 8 + 10);
    stage.setHeight(100);
    
    // show center of parent
    double x = parent.getX() + (parent.getWidth() - stage.getWidth()) / 2;
    double y = parent.getY() + (parent.getHeight() - stage.getHeight()) / 2;
    stage.setX(x);
    stage.setY(y);
    

    完整的loading界面代码如下:

    /**
     * @author itqn
     */
    public class ProgressStage {
    
        private Stage stage;
        private Task<?> work;
    
        private ProgressStage() {
        }
    
        /**
         * 创建
         *
         * @param parent
         * @param work
         * @param ad
         * @return
         */
        public static ProgressStage of(Stage parent, Task<?> work, String ad) {
            ProgressStage ps = new ProgressStage();
            ps.work = Objects.requireNonNull(work);
            ps.initUI(parent, ad);
            return ps;
        }
    
        /**
         * 显示
         */
        public void show() {
            new Thread(work).start();
            stage.show();
        }
    
        private void initUI(Stage parent, String ad) {
            stage = new Stage();
            stage.initOwner(parent);
            // style
            stage.initStyle(StageStyle.UNDECORATED);
            stage.initStyle(StageStyle.TRANSPARENT);
            stage.initModality(Modality.APPLICATION_MODAL);
    
            // message
            Label adLbl = new Label(ad);
            adLbl.setTextFill(Color.BLUE);
    
            // progress
            ProgressIndicator indicator = new ProgressIndicator();
            indicator.setProgress(-1);
            indicator.progressProperty().bind(work.progressProperty());
    
            // pack
            VBox vBox = new VBox();
            vBox.setSpacing(10);
            vBox.setBackground(Background.EMPTY);
            vBox.getChildren().addAll(indicator, adLbl);
    
            // scene
            Scene scene = new Scene(vBox);
            scene.setFill(null);
            stage.setScene(scene);
            stage.setWidth(ad.length() * 8 + 10);
            stage.setHeight(100);
    
            // show center of parent
            double x = parent.getX() + (parent.getWidth() - stage.getWidth()) / 2;
            double y = parent.getY() + (parent.getHeight() - stage.getHeight()) / 2;
            stage.setX(x);
            stage.setY(y);
    
            // close if work finish
            work.setOnSucceeded(e -> stage.close());
        }
    }
    

    loading动画跟Task任务的进度绑定,当Task完成的时候,关闭loading界面。
    这样loading界面组件就完成了。

    接下来,改造之前的视频转码工具代码,将视频转码的代码改为继承Task,而不是Thread,这里Task不需要返回任何信息,所以泛型采用Void即可,然后重写call方法,将耗时的业务代码放在call中执行。

    public class VideoConvertWork extends Task<Void> {
    
        private String ffmpeg;
        private List<TableColumnModel> modelList;
        private Consumer<String> consumer;
    
        public VideoConvertWork(String ffmpeg, List<TableColumnModel> modelList, Consumer<String> consumer) {
            this.ffmpeg = ffmpeg;
            this.modelList = modelList;
            this.consumer = consumer;
        }
    
        @Override
        protected Void call() throws Exception {
            while (true) {
                Optional<TableColumnModel> opt = modelList.stream().filter(i -> !VideoConvertHolder.has(i.getId())).findFirst();
                if (opt.isPresent()) {
                    try {
                        VideoConvertHolder.add(opt.get().getId());
                        convert(opt.get());
                    } catch (Exception e) {
                        e.printStackTrace();
                        Platform.runLater(() -> opt.get().setMessage(e.getMessage()));
                    }
                } else {
                    break;
                }
            }
            return null;
        }
    }
    

    调整“启动”按钮的事件处理:

    public void executeConvertHandler(ActionEvent actionEvent) {
        if (model.getTableList().isEmpty()) {
            new Alert(Alert.AlertType.INFORMATION, "没有转码任务,请选择视频进行转码。").show();
            return;
        }
        if (ffmpeg == null) {
            new Alert(Alert.AlertType.ERROR, "FFmpeg.exe Not Found.").show();
            return;
        }
    //        ((Button) actionEvent.getSource()).setDisable(true);
    //        new VideoConvertExecutor(ffmpeg, model.getTableList(), s -> Platform.runLater(() -> model.setInfo(s))).start();
        ProgressStage.of(
            App.stage,
            new VideoConvertWork(ffmpeg, model.getTableList(), s -> Platform.runLater(() -> model.setInfo(s))),
            "视频转码中..."
        ).show();
    }
    

    loading界面作为通用的组件可以在任何耗时的业务场景下使用,只要将耗时的业务放在Task的call方法中执行即可。

    =========================================================
    关注 公众号 “HiIT青年” 发送 “视频转码工具” 获取转码工具安装包。(如果没有收到回复,可能是你之前取消过关注。)

    HiIT青年
    关注公众号,阅读更多文章。

  • 相关阅读:
    JAVA的显式锁
    JAVA线程池
    多线程中的各种锁
    《深入理解JAVA虚拟机》第三版 第七,八章 要点总结
    《深入理解JAVA虚拟机》第三版 第六章 要点总结
    JVM垃圾收集器总结
    《深入理解JAVA虚拟机》第三版 第二,三章 要点总结
    Map接口的实现类
    博客收藏列表
    毕设开发日志2017-12-28 完成!
  • 原文地址:https://www.cnblogs.com/itqn/p/13543681.html
Copyright © 2011-2022 走看看