如何让歌曲持续时间在 JavaFx 音乐播放器中工作
How to get song duration to work in JavaFx Music Player
我使用 JavaFX 创建了一个音乐播放器。但是,我无法理解如何知道当前正在播放的歌曲的持续时间。
编辑: 我已将以下行添加到我的代码中,现在可以看到歌曲持续时间。但是,当我播放下一首歌曲时,歌曲持续时间不可见。
以下是我添加到代码中的行:
mediaPlayer.currentTimeProperty().addListener(new TimeListener());
TimeListener = new ChangeListener<Duration>()
{
@Override public void changed(ObservableValue<? extends Duration> o, Duration oldVal, Duration newVal)
{
//now newVal is of Duration class
}
};
}
public class TimeListener implements ChangeListener {
public void changed(ObservableValue o, Object oldVal, Object newVal) {
//Update time display with MediaPlayer's current time:
playTime.setText(newVal.toString());
}
}
这是我创建音乐播放器的代码:
public class MusicController implements Initializable
{
@FXML
private ImageView trackLogo;
@FXML
private Button next, previous, pause, play, reset;
@FXML
private Slider VolumeSlider;
@FXML
private AnchorPane Panel;
@FXML
private Label playTime, appTitle, songTitle;
@FXML
private ProgressBar songProgressBar;
@FXML
private Media media;
private MediaPlayer mediaPlayer;
private File directory;
private File [] files;
private ArrayList<File> songs;
private int songNumber;
private Timer timer;
private TimerTask task;
private boolean running;
private ChangeListener<Duration> TimeListener;
@Override
public void initialize(URL url, ResourceBundle rb)
{
songs = new ArrayList<File>();
directory = new File("music");
files = directory.listFiles();
if(files!=null)
{
for(File file : files)
{
songs.add(file);
System.out.println(file);
}
}
media = new Media(songs.get(songNumber).toURI().toString());
mediaPlayer = new MediaPlayer(media);
mediaPlayer.currentTimeProperty().addListener(new TimeListener());
songTitle.setText(songs.get(songNumber).getName());
VolumeSlider.valueProperty().addListener(new ChangeListener<Number>()
{
public void changed(ObservableValue<? extends Number> arg0, Number arg1, Number arg2)
{
mediaPlayer.setVolume(VolumeSlider.getValue() * 0.01);
}
});
TimeListener = new ChangeListener<Duration>()
{
@Override public void changed(ObservableValue<? extends Duration> o, Duration oldVal, Duration newVal)
{
//now newVal is of Duration class
}
};
}
public class TimeListener implements ChangeListener {
public void changed(ObservableValue o, Object oldVal, Object newVal) {
//Update time display with MediaPlayer's current time:
playTime.setText(newVal.toString());
}
}
public void playMedia()
{
beginTimer();
mediaPlayer.setVolume(VolumeSlider.getValue() * 0.01);
mediaPlayer.play();
}
public void pauseMedia()
{
cancelTimer();
mediaPlayer.pause();
}
public void resetMedia()
{
songProgressBar.setProgress(0);
mediaPlayer.seek(Duration.seconds(0));
}
public void previousMedia()
{
if (songNumber > 0)
{
songNumber--;
mediaPlayer.stop();
if(running)
{
cancelTimer();
}
media = new Media(songs.get(songNumber).toURI().toString());
mediaPlayer = new MediaPlayer(media);
songTitle.setText(songs.get(songNumber).getName());
playMedia();
}
else
{
songNumber = songs.size() - 1;
mediaPlayer.stop();
if(running)
{
cancelTimer();
}
media = new Media(songs.get(songNumber).toURI().toString());
mediaPlayer = new MediaPlayer(media);
songTitle.setText(songs.get(songNumber).getName());
playMedia();
}
}
public void nextMedia()
{
if (songNumber < songs.size() - 1)
{
songNumber++;
mediaPlayer.stop();
if(running)
{
cancelTimer();
}
media = new Media(songs.get(songNumber).toURI().toString());
mediaPlayer = new MediaPlayer(media);
songTitle.setText(songs.get(songNumber).getName());
playMedia();
}
else
{
songNumber = 0;
mediaPlayer.stop();
if(running)
{
cancelTimer();
}
media = new Media(songs.get(songNumber).toURI().toString());
mediaPlayer = new MediaPlayer(media);
songTitle.setText(songs.get(songNumber).getName());
playMedia();
}
}
public void beginTimer()
{
timer = new Timer();
task = new TimerTask()
{
public void run()
{
running = true;
double current = mediaPlayer.getCurrentTime().toSeconds();
double end = media.getDuration().toSeconds();
System.out.println(current/end);
songProgressBar.setProgress(current/end);
if (current/end ==1)
{
cancelTimer();
}
}
};
timer.scheduleAtFixedRate(task, 0, 1000);
}
public void cancelTimer()
{
running = false;
timer.cancel();
}
}
我已经尝试阅读 Oracle 文档 (https://docs.oracle.com/javase/8/javafx/media-tutorial/playercontrol.htm) and this youtube video (https://www.youtube.com/watch?v=-D2OIekCKes) 来创建音乐播放器。
如果有任何帮助,我将不胜感激。提前致谢!
你不应该使用 Timer
,或者一般的线程,来做这样的事情。 MediaPlayer
的属性是可观察的,因此您应该向它们添加侦听器,或者将 UI 属性绑定到它们。下面是将 ProgressBar
的进度 属性 绑定到玩家的示例:
progressBar.progressProperty().bind(
Bindings.createDoubleBinding(() -> {
Duration current = mediaPlayer.getCurrentTime();
Duration total = mediaPlayer.getCycleDuration();
return current.toMilli() / total.toMillis();
},
mediaPlayer.currentTimeProperty(),
mediaPlayer.cycleDurationProperty())
);
上面利用了Bindings#createDoubleBinding(Callable,Observable...) and lambda expressions.
每次创建新的 MediaPlayer
时,您都需要执行上述操作,否则将处理使进度条保持最新的问题。你可以对标签做类似的事情,例如,以人类可读的格式显示当前时间和总时间。
此外,除了向 volumeSlider
添加侦听器之外,您还可以这样做:
mediaPlayer.volumeProperty().bind(volumeSlider.valueProperty());
有关详细信息,请参阅 Using JavaFX Properties and Binding。
顺便说一句,你不仅不应该在这里使用 Timer
,而且你使用它的方式也不正确。在 JavaFX 中,有一个名为 JavaFX Application Thread 的特殊线程。只有那个线程应该直接或间接地与 UI 交互。使用任何其他线程与 UI 交互是损坏的代码,并可能导致未定义的行为——JavaFX 不是线程安全的。
您可以使用 Platform#runLater(Runnable)
在 FX 线程上安排操作。
我使用 JavaFX 创建了一个音乐播放器。但是,我无法理解如何知道当前正在播放的歌曲的持续时间。
编辑: 我已将以下行添加到我的代码中,现在可以看到歌曲持续时间。但是,当我播放下一首歌曲时,歌曲持续时间不可见。 以下是我添加到代码中的行:
mediaPlayer.currentTimeProperty().addListener(new TimeListener());
TimeListener = new ChangeListener<Duration>()
{
@Override public void changed(ObservableValue<? extends Duration> o, Duration oldVal, Duration newVal)
{
//now newVal is of Duration class
}
};
}
public class TimeListener implements ChangeListener {
public void changed(ObservableValue o, Object oldVal, Object newVal) {
//Update time display with MediaPlayer's current time:
playTime.setText(newVal.toString());
}
}
这是我创建音乐播放器的代码:
public class MusicController implements Initializable
{
@FXML
private ImageView trackLogo;
@FXML
private Button next, previous, pause, play, reset;
@FXML
private Slider VolumeSlider;
@FXML
private AnchorPane Panel;
@FXML
private Label playTime, appTitle, songTitle;
@FXML
private ProgressBar songProgressBar;
@FXML
private Media media;
private MediaPlayer mediaPlayer;
private File directory;
private File [] files;
private ArrayList<File> songs;
private int songNumber;
private Timer timer;
private TimerTask task;
private boolean running;
private ChangeListener<Duration> TimeListener;
@Override
public void initialize(URL url, ResourceBundle rb)
{
songs = new ArrayList<File>();
directory = new File("music");
files = directory.listFiles();
if(files!=null)
{
for(File file : files)
{
songs.add(file);
System.out.println(file);
}
}
media = new Media(songs.get(songNumber).toURI().toString());
mediaPlayer = new MediaPlayer(media);
mediaPlayer.currentTimeProperty().addListener(new TimeListener());
songTitle.setText(songs.get(songNumber).getName());
VolumeSlider.valueProperty().addListener(new ChangeListener<Number>()
{
public void changed(ObservableValue<? extends Number> arg0, Number arg1, Number arg2)
{
mediaPlayer.setVolume(VolumeSlider.getValue() * 0.01);
}
});
TimeListener = new ChangeListener<Duration>()
{
@Override public void changed(ObservableValue<? extends Duration> o, Duration oldVal, Duration newVal)
{
//now newVal is of Duration class
}
};
}
public class TimeListener implements ChangeListener {
public void changed(ObservableValue o, Object oldVal, Object newVal) {
//Update time display with MediaPlayer's current time:
playTime.setText(newVal.toString());
}
}
public void playMedia()
{
beginTimer();
mediaPlayer.setVolume(VolumeSlider.getValue() * 0.01);
mediaPlayer.play();
}
public void pauseMedia()
{
cancelTimer();
mediaPlayer.pause();
}
public void resetMedia()
{
songProgressBar.setProgress(0);
mediaPlayer.seek(Duration.seconds(0));
}
public void previousMedia()
{
if (songNumber > 0)
{
songNumber--;
mediaPlayer.stop();
if(running)
{
cancelTimer();
}
media = new Media(songs.get(songNumber).toURI().toString());
mediaPlayer = new MediaPlayer(media);
songTitle.setText(songs.get(songNumber).getName());
playMedia();
}
else
{
songNumber = songs.size() - 1;
mediaPlayer.stop();
if(running)
{
cancelTimer();
}
media = new Media(songs.get(songNumber).toURI().toString());
mediaPlayer = new MediaPlayer(media);
songTitle.setText(songs.get(songNumber).getName());
playMedia();
}
}
public void nextMedia()
{
if (songNumber < songs.size() - 1)
{
songNumber++;
mediaPlayer.stop();
if(running)
{
cancelTimer();
}
media = new Media(songs.get(songNumber).toURI().toString());
mediaPlayer = new MediaPlayer(media);
songTitle.setText(songs.get(songNumber).getName());
playMedia();
}
else
{
songNumber = 0;
mediaPlayer.stop();
if(running)
{
cancelTimer();
}
media = new Media(songs.get(songNumber).toURI().toString());
mediaPlayer = new MediaPlayer(media);
songTitle.setText(songs.get(songNumber).getName());
playMedia();
}
}
public void beginTimer()
{
timer = new Timer();
task = new TimerTask()
{
public void run()
{
running = true;
double current = mediaPlayer.getCurrentTime().toSeconds();
double end = media.getDuration().toSeconds();
System.out.println(current/end);
songProgressBar.setProgress(current/end);
if (current/end ==1)
{
cancelTimer();
}
}
};
timer.scheduleAtFixedRate(task, 0, 1000);
}
public void cancelTimer()
{
running = false;
timer.cancel();
}
}
我已经尝试阅读 Oracle 文档 (https://docs.oracle.com/javase/8/javafx/media-tutorial/playercontrol.htm) and this youtube video (https://www.youtube.com/watch?v=-D2OIekCKes) 来创建音乐播放器。
如果有任何帮助,我将不胜感激。提前致谢!
你不应该使用 Timer
,或者一般的线程,来做这样的事情。 MediaPlayer
的属性是可观察的,因此您应该向它们添加侦听器,或者将 UI 属性绑定到它们。下面是将 ProgressBar
的进度 属性 绑定到玩家的示例:
progressBar.progressProperty().bind(
Bindings.createDoubleBinding(() -> {
Duration current = mediaPlayer.getCurrentTime();
Duration total = mediaPlayer.getCycleDuration();
return current.toMilli() / total.toMillis();
},
mediaPlayer.currentTimeProperty(),
mediaPlayer.cycleDurationProperty())
);
上面利用了Bindings#createDoubleBinding(Callable,Observable...) and lambda expressions.
每次创建新的 MediaPlayer
时,您都需要执行上述操作,否则将处理使进度条保持最新的问题。你可以对标签做类似的事情,例如,以人类可读的格式显示当前时间和总时间。
此外,除了向 volumeSlider
添加侦听器之外,您还可以这样做:
mediaPlayer.volumeProperty().bind(volumeSlider.valueProperty());
有关详细信息,请参阅 Using JavaFX Properties and Binding。
顺便说一句,你不仅不应该在这里使用 Timer
,而且你使用它的方式也不正确。在 JavaFX 中,有一个名为 JavaFX Application Thread 的特殊线程。只有那个线程应该直接或间接地与 UI 交互。使用任何其他线程与 UI 交互是损坏的代码,并可能导致未定义的行为——JavaFX 不是线程安全的。
您可以使用 Platform#runLater(Runnable)
在 FX 线程上安排操作。