文档



JavaFX:将媒体资产整合到JavaFX应用程序中

3 控制媒体播放

在本节中,您将创建一个具有图形界面元素的完全功能媒体播放器,用于控制播放。

要创建媒体播放器,您需要实现三个嵌套媒体对象的结构,编码图形控件,并添加一些用于播放功能的逻辑,如下图所示:图3-1

图3-1 带有播放控件的媒体播放器结构

带有UI控件的媒体播放器的逻辑结构。
"图3-1 带有播放控件的媒体播放器结构"的描述

您将逐步添加播放控件到您在第2章“将媒体嵌入网页”中创建的媒体播放器。如果您还没有完成该媒体播放器应用程序,请在继续本章的其余部分之前完成。您添加的媒体控制面板包括三个元素:playButton、progress和volumeControl。

创建控件

在这个部分中,您将创建一个新的JavaFX源文件MediaControl.java,该文件将包含用于播放/暂停、进度和音量功能的面板和UI控件。

  1. 在NetBeans IDE中打开EmbeddedMediaPlayer作为主项目,创建一个新的JavaFX文件并添加到项目中。

    1. 使用Ctrl+N或选择IDE主菜单中的文件>新建文件。

    2. 选择JavaFX类别和JavaFX主类文件类型。点击下一步。

    3. 在名称和位置对话框中,在类名字段中输入MediaControl

    4. 在包字段中,从下拉列表中选择embeddedmediaplayer,然后点击完成。

  2. MediaControl.java源文件中,删除package embeddedmediaplayer行之后的所有行。

  3. 示例3-1中显示的导入语句添加到文件的顶部。

    示例3-1 添加的导入语句

    import javafx.scene.control.Label;
    import javafx.scene.control.Slider;
    import javafx.scene.layout.BorderPane;
    import javafx.scene.layout.HBox;
    import javafx.scene.layout.Pane;
    import javafx.scene.media.MediaPlayer;
    import javafx.scene.media.MediaView;
    import javafx.util.Duration;
    
  4. 复制并粘贴示例3-2中的代码行,创建将容纳控件的BorderPane。

    示例3-2 添加MediaControl类代码

    public class MediaControl extends BorderPane {
        private MediaPlayer mp;
        private MediaView mediaView;
        private final boolean repeat = false;
        private boolean stopRequested = false;
        private boolean atEndOfMedia = false;
        private Duration duration;
        private Slider timeSlider;
        private Label playTime;
        private Slider volumeSlider;
        private HBox mediaBar;
    
        public MediaControl(final MediaPlayer mp) {
            this.mp = mp;
            setStyle("-fx-background-color: #bfc2c7;");
            mediaView = new MediaView(mp);
            Pane mvPane = new Pane() {                };
            mvPane.getChildren().add(mediaView);
            mvPane.setStyle("-fx-background-color: black;"); 
            setCenter(mvPane);
         }
    }
    
  5. 复制并粘贴示例3-3中的代码行,将它们粘贴到setCenter(mvPane)行之后。这段代码添加了媒体工具栏和播放按钮。

    示例3-3 添加媒体工具栏和播放按钮

    mediaBar = new HBox();
            mediaBar.setAlignment(Pos.CENTER);
            mediaBar.setPadding(new Insets(5, 10, 5, 10));
            BorderPane.setAlignment(mediaBar, Pos.CENTER);
     
            final Button playButton  = new Button(">");
            mediaBar.getChildren().add(playButton);
            setBottom(mediaBar); 
         }
    }
    
  6. 示例3-4中显示的导入语句添加到导入语句列表的顶部。

    示例3-4 添加更多导入语句

    import javafx.geometry.Insets;
    import javafx.geometry.Pos;
    import javafx.scene.control.Button;
    
  7. 将其余的UI控件添加到控制面板。将代码行示例3-5放在mediaBar.getChildren().add(playButton);行之后,setBottom(mediaBar)行之前。

    示例3-5 添加其余的UI控件

    // 添加间隔
    Label spacer = new Label("   ");
    mediaBar.getChildren().add(spacer);
     
    // 添加时间标签
    Label timeLabel = new Label("时间:");
    mediaBar.getChildren().add(timeLabel);
     
    // 添加时间滑块
    timeSlider = new Slider();
    HBox.setHgrow(timeSlider,Priority.ALWAYS);
    timeSlider.setMinWidth(50);
    timeSlider.setMaxWidth(Double.MAX_VALUE);
    mediaBar.getChildren().add(timeSlider);
    
    // 添加播放时间标签
    playTime = new Label();
    playTime.setPrefWidth(130);
    playTime.setMinWidth(50);
    mediaBar.getChildren().add(playTime);
     
    // 添加音量标签
    Label volumeLabel = new Label("音量:");
    mediaBar.getChildren().add(volumeLabel);
     
    // 添加音量滑块
    volumeSlider = new Slider();        
    volumeSlider.setPrefWidth(70);
    volumeSlider.setMaxWidth(Region.USE_PREF_SIZE);
    volumeSlider.setMinWidth(30);
     
    mediaBar.getChildren().add(volumeSlider);
    
  8. 示例3-6中显示的更多导入语句添加到文件的顶部。

    示例3-6 添加更多导入语句

    import javafx.scene.layout.Priority;
    import javafx.scene.layout.Region;
    

添加功能逻辑代码

在所有控件都被创建并添加到控制面板之后,添加功能逻辑来管理媒体播放并使应用程序具有交互性。

  1. 为播放按钮添加事件处理程序和监听器。将代码行示例3-7中的代码复制并粘贴到final Button playButton = new Button(">")行之后和mediaBar.getChildren().add(playButton)行之前。

    示例3-7 添加播放按钮的事件处理程序和监听器

    playButton.setOnAction(new EventHandler<ActionEvent>() {
        public void handle(ActionEvent e) {
            Status status = mp.getStatus();
     
            if (status == Status.UNKNOWN  || status == Status.HALTED)
            {
               // 在这些状态下不执行任何操作
               return;
            }
     
              if ( status == Status.PAUSED
                 || status == Status.READY
                 || status == Status.STOPPED)
              {
                 // 如果在影片末尾,倒回影片
                 if (atEndOfMedia) {
                    mp.seek(mp.getStartTime());
                    atEndOfMedia = false;
                 }
                 mp.play();
                 } else {
                   mp.pause();
                 }
             }
       });
    
  2. 为了避免出现错误,你可以在之前添加代码时添加导入语句。但是这次,为了消除所有标记的错误,按下Ctrl+Shift+I或右键单击任意位置并选择Fix Imports。在Fix All Imports对话框中,从下拉菜单中选择javafx.scene.media.MediaPlayer.Status, javafx.event.ActionEventjavafx.event.EventHandler。点击OK。

  3. 在从示例3-7中添加的代码行之后和mediaBar.getChildren().add(playButton)行之前添加以下代码。这段代码将处理监听器。

    示例3-8 添加监听器代码

    mp.currentTimeProperty().addListener(new InvalidationListener() 
            {
                public void invalidated(Observable ov) {
                    updateValues();
                }
            });
     
            mp.setOnPlaying(new Runnable() {
                public void run() {
                    if (stopRequested) {
                        mp.pause();
                        stopRequested = false;
                    } else {
                        playButton.setText("||");
                    }
                }
            });
     
            mp.setOnPaused(new Runnable() {
                public void run() {
                    System.out.println("onPaused");
                    playButton.setText(">");
                }
            });
     
            mp.setOnReady(new Runnable() {
                public void run() {
                    duration = mp.getMedia().getDuration();
                    updateValues();
                }
            });
     
            mp.setCycleCount(repeat ? MediaPlayer.INDEFINITE : 1);
            mp.setOnEndOfMedia(new Runnable() {
                public void run() {
                    if (!repeat) {
                        playButton.setText(">");
                        stopRequested = true;
                        atEndOfMedia = true;
                    }
                }
           });
    

    请注意,出现的错误将在下一步中通过添加更多代码来修复。

  4. timeSlider.setMaxWidth(Double.MAX_VALUE)这一行后面,mediaBar.getChildren().add(timeSlider)这一行前面,添加以下代码片段来为时间滑块添加监听器。

    示例 3-9 添加时间滑块监听器

    timeSlider.valueProperty().addListener(new InvalidationListener() {
        public void invalidated(Observable ov) {
           if (timeSlider.isValueChanging()) {
           // 将持续时间乘以滑块位置计算出的百分比
              mp.seek(duration.multiply(timeSlider.getValue() / 100.0));
           }
        }
    });
    
  5. volumeSlider.setMinWidth(30)这一行后面,mediabar.getChildren().add(volumeSlider)这一行前面,添加以下代码片段来为音量滑块控件添加监听器。

    示例 3-10 添加音量控制监听器

    volumeSlider.valueProperty().addListener(new InvalidationListener() {
        public void invalidated(Observable ov) {
           if (volumeSlider.isValueChanging()) {
               mp.setVolume(volumeSlider.getValue() / 100.0);
           }
        }
    });
    
  6. 创建一个名为updateValues的方法,用于播放控件的更新。将其添加到public MediaControl()方法之后。

    示例 3-11 添加 updateValues 方法

    protected void updateValues() {
      if (playTime != null && timeSlider != null && volumeSlider != null) {
         Platform.runLater(new Runnable() {
            public void run() {
              Duration currentTime = mp.getCurrentTime();
              playTime.setText(formatTime(currentTime, duration));
              timeSlider.setDisable(duration.isUnknown());
              if (!timeSlider.isDisabled() 
                && duration.greaterThan(Duration.ZERO) 
                && !timeSlider.isValueChanging()) {
                  timeSlider.setValue(currentTime.divide(duration).toMillis()
                      * 100.0);
              }
              if (!volumeSlider.isValueChanging()) {
                volumeSlider.setValue((int)Math.round(mp.getVolume() 
                      * 100));
              }
            }
         });
      }
    }
    
  7. 在updateValues()方法之后添加private方法formatTime()。formatTime()方法计算媒体播放的经过时间,并将其格式化以在控制工具栏上显示。

    示例3-12 添加计算经过时间的方法

    private static String formatTime(Duration elapsed, Duration duration) {
       int intElapsed = (int)Math.floor(elapsed.toSeconds());
       int elapsedHours = intElapsed / (60 * 60);
       if (elapsedHours > 0) {
           intElapsed -= elapsedHours * 60 * 60;
       }
       int elapsedMinutes = intElapsed / 60;
       int elapsedSeconds = intElapsed - elapsedHours * 60 * 60 
                               - elapsedMinutes * 60;
     
       if (duration.greaterThan(Duration.ZERO)) {
          int intDuration = (int)Math.floor(duration.toSeconds());
          int durationHours = intDuration / (60 * 60);
          if (durationHours > 0) {
             intDuration -= durationHours * 60 * 60;
          }
          int durationMinutes = intDuration / 60;
          int durationSeconds = intDuration - durationHours * 60 * 60 - 
              durationMinutes * 60;
          if (durationHours > 0) {
             return String.format("%d:%02d:%02d/%d:%02d:%02d", 
                elapsedHours, elapsedMinutes, elapsedSeconds,
                durationHours, durationMinutes, durationSeconds);
          } else {
              return String.format("%02d:%02d/%02d:%02d",
                elapsedMinutes, elapsedSeconds,durationMinutes, 
                    durationSeconds);
          }
          } else {
              if (elapsedHours > 0) {
                 return String.format("%d:%02d:%02d", elapsedHours, 
                        elapsedMinutes, elapsedSeconds);
                } else {
                    return String.format("%02d:%02d",elapsedMinutes, 
                        elapsedSeconds);
                }
            }
        }
    
  8. 最后,修复导入。在任何空白处右键单击,从“修复所有导入”对话框中选择javafx.application.Platformjavafx.beans.Observable。点击确定。

修改EmbeddedMediaPlayer.java

要添加控件,修改之前章节中创建的EmbeddedMediaPlayer.java文件,并添加代码以添加MediaControl对象。

  1. 复制示例3-13中的代码行,并将其粘贴到mediaPlayer.setAutoPlay(true)行之后。

    示例3-13 添加源代码以创建MediaControl对象

    MediaControl mediaControl = new MediaControl(mediaPlayer);
    scene.setRoot(mediaControl);
    
  2. 删除示例3-14中显示的三行代码,这些代码之前创建了mediaViewmediaPlayer对象。

    示例3-14 删除代码行

    // create mediaView and add media player to the viewer
     MediaView mediaView = new MediaView(mediaPlayer);
         ((Group)scene.getRoot()).getChildren().add(mediaView);
    
  3. 删除MediaView的导入语句:import javafx.scene.media.MediaView;

  4. 调整场景的高度以适应媒体控件的添加。

    示例3-15 更改场景的高度

    Scene scene = new Scene(root, 540, 241);
    

编译和运行EmbeddedMedia

现在构建刚刚在前一节中创建的应用程序并运行它。

  1. 右键单击EmbeddedMediaPlayer项目节点,然后选择Clean and Build。

  2. 如果没有构建错误,再次右键单击节点,然后选择Run。媒体播放器与控件出现,类似于图3-2,并开始播放。

    图3-2 带有播放控件的媒体播放器

    图3-2的描述
    "图3-2 带有播放控件的媒体播放器"的描述

  3. 使用播放/暂停控制按钮停止和恢复视频。使用进度条前进或后退视频。使用音量控制按钮调整音量。

    EmbeddedMediaPlayer.zip文件中找到完整的应用程序代码。

关闭窗口

目录

JavaFX: 将媒体资源整合到JavaFX应用程序中

展开 | 折叠