I'm new to Java and JavaFX -- I'm making a small player application and are finding it a challenge to get the duration timer to display on a label on my display.
My latest attempt was creating a TimeListener.java class which would change the duration values for each new song played and set them on the label in another class but that idea is flawed as I came across a non-static error.
TrackPlayer object class
private MediaPlayer player;
private Media track;
private String filepath;
private Duration duration;
public TrackPlayer(String filepath) {
this.filepath = filepath;
track = new Media(filepath);
player = new MediaPlayer(track);
player.setOnReady(() -> {
duration = track.getDuration();
System.out.println("Duration: " + duration);
});
player.currentTimeProperty().addListener(new TimeListener());
}
TimeListener class
public class TimeListener implements ChangeListener<Duration> {
@Override
public void changed(ObservableValue<? extends Duration> observable, Duration oldValue, Duration newValue) {
TrackPlayerController.setTime(newValue.toString());
}
}
FXML Controller class
@FXML
private Label runTime;
...
public void setTime(String time) {
//runTime.setText(time);
}
How else could I approach this problem? I want a label which would display something like 00:00:00 (elapsed) / 00:00:00 (duration) but I'm sure if I just get the duration working I can also get the elapsed time working.
Example with the same problem but most if not all features removed
TrackPlayer class
package logic;
import javafx.beans.property.ReadOnlyObjectProperty;
import javafx.scene.media.Media;
import javafx.scene.media.MediaPlayer;
import javafx.scene.media.MediaPlayer.Status;
import javafx.util.Duration;
public class TrackPlayer {
private MediaPlayer player;
private Media track;
private String filepath;
private Duration duration;
public TrackPlayer(String filepath) {
this.filepath = filepath;
track = new Media(filepath);
player = new MediaPlayer(track);
player.setOnReady(() -> {
duration = track.getDuration();
System.out.println("Duration: " + duration);
});
}
public void playSong() {
System.out.println("Playing song");
player.play();
}
public void pauseSong() {
System.out.println("Pausing song");
player.pause();
}
public void stopSong() {
System.out.println("Stopping song");
player.stop();
}
public Status getStatus() {
return player.getStatus();
}
public Duration getDuration() {
return duration;
}
public Duration getCurrentTime() {
return player.getCurrentTime();
}
public Duration getStartTime() {
return player.getStartTime();
}
public void setSeek(Duration duration) {
player.seek(duration);
}
public Media getMedia() {
return player.getMedia();
}
public ReadOnlyObjectProperty<Duration> currentTimeProperty() {
return player.currentTimeProperty();
}
public Duration getTotalDuration() {
return player.getTotalDuration();
}
}
TrackPlayerController class
package gui;
import java.io.IOException;
import java.net.URL;
import java.util.ResourceBundle;
import javafx.event.ActionEvent;
import javafx.fxml.FXML;
import javafx.fxml.Initializable;
import javafx.scene.control.Label;
import javafx.scene.control.TableColumn;
import javafx.scene.control.TableRow;
import javafx.scene.control.TableView;
import javafx.scene.control.cell.PropertyValueFactory;
import logic.TrackPlayer;
import logic.Track;
public class TrackPlayerController implements Initializable {
@FXML
private TableView<Track> playingTable;
@FXML
private TableColumn<Track, String> playingTitleCol;
@FXML
private TableColumn<Track, String> playingArtistCol;
@FXML
private TableColumn<Track, String> playingGenreCol;
@FXML
private TableColumn<Track, String> playingRunTimeCol;
@FXML
private Label runTime;
private TrackPlayer player;
@Override
public void initialize(URL fxmlFileLocation, ResourceBundle resources) {
playingTitleCol.setCellValueFactory(new PropertyValueFactory<>("TrackTitle"));
playingArtistCol.setCellValueFactory(new PropertyValueFactory<>("TrackArtist"));
playingGenreCol.setCellValueFactory(new PropertyValueFactory<>("TrackGenre"));
playingRunTimeCol.setCellValueFactory(new PropertyValueFactory<>("RunTime"));
player.currentTimeProperty().addListener(observable -> {
setTime(player.getCurrentTime()
+ " / "
+ player.getTotalDuration());
});
playingTable.setRowFactory(tv -> { // Function for double-click to play (load)
TableRow<Track> row = new TableRow<>();
row.setOnMouseClicked(event -> {
if (event.getClickCount() == 2 && (!row.isEmpty())) {
play();
}
});
return row;
});
}
@FXML
private void play() {
}
@FXML
private void reset(ActionEvent e) {
}
@FXML
private void remove(ActionEvent e) {
}
@FXML
private void removeAll(ActionEvent e) {
}
@FXML
private void search(ActionEvent e) throws IOException {
}
public void setTime(String time) {
runTime.setText(time);
}
}
TrackPlayerMain class
package gui;
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.stage.Stage;
import javafx.fxml.FXMLLoader;
import javafx.scene.layout.BorderPane;
public class TrackPlayerMain extends Application {
@Override
public void start(Stage primaryStage) throws Exception {
BorderPane root = new BorderPane();
FXMLLoader trackPlayerLoader = new FXMLLoader(getClass().getResource("TrackPlayer.fxml"));
root.setCenter(trackPlayerLoader.load());
TrackPlayerController trackPlayerController = trackPlayerLoader.getController();
Scene scene = new Scene(root, 600, 400);
primaryStage.setScene(scene);
primaryStage.show();
}
public static void main(String[] args) {
launch(args);
}
}
TrackPlayer FXML
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.scene.paint.*?>
<?import javafx.scene.text.*?>
<?import java.lang.*?>
<?import java.util.*?>
<?import javafx.scene.*?>
<?import javafx.scene.control.*?>
<?import javafx.scene.layout.*?>
<AnchorPane maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" prefHeight="400.0" prefWidth="600.0" xmlns="http://javafx.com/javafx/8" xmlns:fx="http://javafx.com/fxml/1" fx:controller="gui.TrackPlayerController">
<children>
<Slider fx:id="timeSlider" layoutX="9.0" layoutY="333.0" prefHeight="25.0" prefWidth="582.0" />
<Label alignment="BOTTOM_LEFT" layoutX="23.0" layoutY="10.0" prefHeight="17.0" prefWidth="75.0" text="Now Playing" />
<Button fx:id="play" layoutX="250.0" layoutY="361.0" mnemonicParsing="false" onAction="#play" prefHeight="25.0" prefWidth="100.0" text="Play" />
<Button fx:id="ff" layoutX="356.0" layoutY="361.0" mnemonicParsing="false" text=">>" />
<Button fx:id="rw" layoutX="211.0" layoutY="361.0" mnemonicParsing="false" text="<<" />
<Button fx:id="reset" layoutX="22.0" layoutY="361.0" mnemonicParsing="false" onAction="#reset" prefWidth="59.0" text="Reset" />
<Button fx:id="remove" layoutX="498.0" layoutY="305.0" mnemonicParsing="false" onAction="#remove" prefWidth="83.0" text="Remove" />
<Label fx:id="runTime" alignment="TOP_CENTER" layoutX="516.0" layoutY="350.0" prefHeight="17.0" prefWidth="75.0" text="00:00 / 00:00" textFill="#00000065">
<font>
<Font size="11.0" />
</font>
</Label>
<Button fx:id="removeAll" layoutX="401.0" layoutY="305.0" mnemonicParsing="false" onAction="#removeAll" prefHeight="25.0" prefWidth="83.0" text="Remove All" />
<TableView fx:id="playingTable" layoutX="18.0" layoutY="32.0" prefHeight="263.0" prefWidth="563.0">
<columns>
<TableColumn fx:id="playingTitleCol" editable="false" prefWidth="140.75" resizable="false" text="Title" />
<TableColumn fx:id="playingArtistCol" editable="false" prefWidth="140.75" resizable="false" text="Artist" />
<TableColumn fx:id="playingGenreCol" editable="false" prefWidth="140.75" resizable="false" text="Genre" />
<TableColumn fx:id="playingRunTimeCol" prefWidth="140.75" resizable="false" text="Run Time" />
</columns>
<columnResizePolicy>
<TableView fx:constant="CONSTRAINED_RESIZE_POLICY" />
</columnResizePolicy>
</TableView>
<Button fx:id="search" layoutX="303.0" layoutY="305.0" mnemonicParsing="false" onAction="#search" prefHeight="0.0" prefWidth="83.0" text="Search" />
</children>
</AnchorPane>
From what I assume it is throwing NullPointerException because it's trying to initalize the listener with the current time and duration however the player object has not been created yet (as no song is played right from the start, only when selected and pressed play) -- if that is the case how can I add the listener?
Edit: Okay so I've tested what causes the NullPointerException and it's the player being null, as the program launches when I do this.
if (player != null) {
player.currentTimeProperty().addListener(observable -> {
runTime.setText(player.getCurrentTime()
+ " / "
+ player.getTotalDuration());
});
}
However when I do this the listener doesn't get initialized as the runTime label does not change at all. This is my problem that I'm trying to solve. How can I go about fixing it?