Documentation



JavaFX: Incorporating Media Assets Into JavaFX Applications

3 Controlling Media Playback

In this section you create a full-functional media player with graphical UI elements that control the playback.

To create a media player you need to implement the structure of the three nested media objects, encode graphical controls, and add some logic for playback functions, as illustrated in the Figure 3-1 below.

Figure 3-1 Structure of Media Player with Playback Controls

Logical structure of a media player with UI controls.
Description of "Figure 3-1 Structure of Media Player with Playback Controls"

You step through adding playback controls to the media player that you created in Chapter 2, "Embedding Media Into a Web Page". If you haven't already done so, complete that media player application before proceeding with the rest of this chapter. The media control panel you add consists of three elements: playButton, progress, and volumeControl.

Creating Controls

In this section you create a new JavaFX source file, MediaControl.java, that will contain the pane and UI controls for the play/pause, progress, and volume features.

  1. With the EmbeddedMediaPlayer opened as the main project in the NetBeans IDE, create a new JavaFX file to add to the project.

    1. Use Ctrl+N or select File > New File from the IDE's main menu.

    2. Select Category JavaFX and file type JavaFX Main class. Click Next.

    3. In the Name and Location dialog, type MediaControl in the Class Name field.

    4. In the Package field, select embeddedmediaplayer from the drop-down list and click Finish.

  2. In the MediaControl.java source file, delete all the lines after package embeddedmediaplayer line.

  3. Add the import statements that are shown in Example 3-1 to the top of the file.

    Example 3-1 Import Statements to Add

    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. Copy and paste the lines of code in Example 3-2 to create the BorderPane that will hold the controls.

    Example 3-2 Add MediaControl Class Code

    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. Copy the lines of code in Example 3-3 and paste them immediately after the line that says setCenter(mvPane). This code adds the Media toolbar and the Play button.

    Example 3-3 Add Media Toolbar and Play Button

    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. Add the import statements shown in Example 3-4 to the top of the list of import statements.

    Example 3-4 Add More Import Statements

    import javafx.geometry.Insets;
    import javafx.geometry.Pos;
    import javafx.scene.control.Button;
    
  7. Add the remainder of the UI controls to the control pane. Put the lines of code in Example 3-5 after the mediaBar.getChildren().add(playButton); line and before the setBottom(mediaBar) line.

    Example 3-5 Add the Rest of the UI Controls

    // Add spacer
    Label spacer = new Label("   ");
    mediaBar.getChildren().add(spacer);
     
    // Add Time label
    Label timeLabel = new Label("Time: ");
    mediaBar.getChildren().add(timeLabel);
     
    // Add time slider
    timeSlider = new Slider();
    HBox.setHgrow(timeSlider,Priority.ALWAYS);
    timeSlider.setMinWidth(50);
    timeSlider.setMaxWidth(Double.MAX_VALUE);
    mediaBar.getChildren().add(timeSlider);
    
    // Add Play label
    playTime = new Label();
    playTime.setPrefWidth(130);
    playTime.setMinWidth(50);
    mediaBar.getChildren().add(playTime);
     
    // Add the volume label
    Label volumeLabel = new Label("Vol: ");
    mediaBar.getChildren().add(volumeLabel);
     
    // Add Volume slider
    volumeSlider = new Slider();        
    volumeSlider.setPrefWidth(70);
    volumeSlider.setMaxWidth(Region.USE_PREF_SIZE);
    volumeSlider.setMinWidth(30);
     
    mediaBar.getChildren().add(volumeSlider);
    
  8. Add more import statements shown in Example 3-6 to the top of the file.

    Example 3-6 Add More Import Statements

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

Add the Functional Logic Code

After all the controls have been created and added to the control panel, add the functional logic to manage the media playback and make your application interactive.

  1. Add the event handler and listener for the Play button. Copy and paste the lines of code in Example 3-7 after the final Button playButton = new Button(">") line and before the mediaBar.getChildren().add(playButton) line.

    Example 3-7 Add Play Button's Event Handler and Listener

    playButton.setOnAction(new EventHandler<ActionEvent>() {
        public void handle(ActionEvent e) {
            Status status = mp.getStatus();
     
            if (status == Status.UNKNOWN  || status == Status.HALTED)
            {
               // don't do anything in these states
               return;
            }
     
              if ( status == Status.PAUSED
                 || status == Status.READY
                 || status == Status.STOPPED)
              {
                 // rewind the movie if we're sitting at the end
                 if (atEndOfMedia) {
                    mp.seek(mp.getStartTime());
                    atEndOfMedia = false;
                 }
                 mp.play();
                 } else {
                   mp.pause();
                 }
             }
       });
    
  2. The import statements used by the code you just added from Example 3-7 could have been added beforehand to avoid getting errors. But this time, to eliminate all of marked errors, press Ctrl+Shift+I or right-click anywhere and select Fix Imports. From the Fix All Imports dialog, select javafx.scene.media.MediaPlayer.Status, javafx.event.ActionEvent, and javafx.event.EventHandler from the drop-down menus. Click OK.

  3. Add the following lines of code immediately after the lines of code you added from Example 3-7 and before the line that says mediaBar.getChildren().add(playButton). This code will handle the listener.

    Example 3-8 Add Listener Code

    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;
                    }
                }
           });
    

    Note that the errors that appear will be fixed by adding more code in the next steps.

  4. Add listener for the time slider by adding the following code snippet after the line that says timeSlider.setMaxWidth(Double.MAX_VALUE) and before the line that says mediaBar.getChildren().add(timeSlider).

    Example 3-9 Add Listener for Time Slider

    timeSlider.valueProperty().addListener(new InvalidationListener() {
        public void invalidated(Observable ov) {
           if (timeSlider.isValueChanging()) {
           // multiply duration by percentage calculated by slider position
              mp.seek(duration.multiply(timeSlider.getValue() / 100.0));
           }
        }
    });
    
  5. Add listener for the volume slider control by adding the following code snippet after the line that says volumeSlider.setMinWidth(30) and before the mediabar.getChildren().add(volumeSlider).

    Example 3-10 Add Listener for the Volume Control

    volumeSlider.valueProperty().addListener(new InvalidationListener() {
        public void invalidated(Observable ov) {
           if (volumeSlider.isValueChanging()) {
               mp.setVolume(volumeSlider.getValue() / 100.0);
           }
        }
    });
    
  6. Create Method updateValues used by the playback controls. Add it after the public MediaControl() method.

    Example 3-11 Add UpdateValues Method

    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. Add the private method formatTime() after the updateValues() method. The formatTime() method calculates the elapsed time the media has been playing and formats it to be displayed on the control toolbar.

    Example 3-12 Add Method for Calculating Elapsed Time

    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. Lastly, fix the imports. Right-click in any white space and select javafx.application.Platform and javafx.beans.Observable from the Fix All Imports dialog. Click OK.

Modify the EmbeddedMediaPlayer.java

To add the control, modify the EmbeddedMediaPlayer.java file that you created in the previous chapter and add the code to add the MediaControl object.

  1. Copy the lines of code in Example 3-13 and paste them right after the mediaPlayer.setAutoPlay(true) line.

    Example 3-13 Add the Source Code to Create MediaControl Object

    MediaControl mediaControl = new MediaControl(mediaPlayer);
    scene.setRoot(mediaControl);
    
  2. Delete the three lines shown in Example 3-14, which previously created the mediaView and mediaPlayer objects.

    Example 3-14 Delete Lines of Code

    // create mediaView and add media player to the viewer
     MediaView mediaView = new MediaView(mediaPlayer);
         ((Group)scene.getRoot()).getChildren().add(mediaView);
    
  3. Delete the import statement for the MediaView: import javafx.scene.media.MediaView;

  4. Adjust the size of the scene's height to accommodate the addition of the media controls.

    Example 3-15 Change the Scene's Height

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

Compile and Run the EmbeddedMedia

Now build the application you just created in the previous section and run it.

  1. Right-click the EmbeddedMediaPlayer project node and select Clean and Build.

  2. If there are no build errors, right-click the node again and select Run. The media player with control appears, similar to Figure 3-2 and begins to play.

    Figure 3-2 Media Player with Playback Controls

    Description of Figure 3-2 follows
    Description of "Figure 3-2 Media Player with Playback Controls"

  3. Stop and resume the video using the play/pause control button. Move forwards or backwards through the video using the progress bar. Adjust the volume using the volume control button.

    Find the complete application code in the EmbeddedMediaPlayer.zip file.

Close Window

Table of Contents

JavaFX: Incorporating Media Assets Into JavaFX Applications

Expand | Collapse