How to create custom binding types in JavaFX
I have a custom class named timeelapsed (it is immutable) Timeelapsed has a constructor that accepts a duration (which is the type used to track time in JavaFX mediaplayer) The constructor then converts the duration to timeelapsed
The problem is that I have a function that needs to return timeelapsed observablevalue What I need is to be able to do such things:
new Binding<TimeElapsed>() {
{
super.bind(player.duration())
}
@Override
protected TimeElapsed computeValue() {
return new TimeElapsed(player.duration());
}
}
But for some reason, without binding generics, you can only use doublebinding and similar things. You calculate values there, but you can't select types So what should I do?
Solution
Use the sample solution of objectbinding < timeelapsed >
Key methods
/* @return an ObjectBinding of immutable TimeElapsed objects for the player */
private ObjectBinding<TimeElapsed> createElapsedBindingByBindingsAPI(
final MediaPlayer player
) {
return Bindings.createObjectBinding(
new Callable<TimeElapsed>() {
@Override
public TimeElapsed call() throws Exception {
return new TimeElapsed(player.getCurrentTime());
}
},player.currentTimeproperty()
);
}
Complete executable example
Change media in the sample_ Path to match the media you need
import javafx.application.Application;
import javafx.beans.binding.*;
import javafx.scene.Scene;
import javafx.scene.control.Label;
import javafx.scene.layout.StackPane;
import javafx.scene.media.*;
import javafx.stage.Stage;
import javafx.util.Duration;
import java.io.File;
import java.net.MalformedURLException;
import java.util.concurrent.Callable;
/** Displays progress (time elapsed in seconds) of playing a media file. */
public class TimeElapsedBinding extends Application {
private static final String MEDIA_PATH =
"C:\\Users\\Public\\Music\\Sample Music\\Dillon - Thirteen Thirtyfive.mp3";
public static void main(String[] args) { launch(args); }
@Override public void start(Stage stage) throws Exception {
final MediaView mediaView = createMediaView();
final MediaPlayer player = mediaView.getMediaPlayer();
final Label elapsedLabel = new Label();
ObjectBinding<TimeElapsed> elapsedBinding =
createElapsedBindingByBindingsAPI(player);
StringBinding elapsedStringBinding =
createStringBindingByBindingsAPI(elapsedBinding);
elapsedLabel.textproperty().bind(
elapsedStringBinding
);
StackPane layout = new StackPane();
layout.setStyle("-fx-background-color: cornsilk; -fx-padding: 20px;");
layout.getChildren().setAll(
mediaView,elapsedLabel
);
stage.setScene(new Scene(layout));
stage.show();
}
/* @return an ObjectBinding of immutable TimeElapsed objects for the player */
private ObjectBinding<TimeElapsed> createElapsedBindingByBindingsAPI(
final MediaPlayer player
) {
return Bindings.createObjectBinding(
new Callable<TimeElapsed>() {
@Override
public TimeElapsed call() throws Exception {
return new TimeElapsed(player.getCurrentTime());
}
},player.currentTimeproperty()
);
}
/* @return a string binding to an ObjectBinding of immutable TimeElapsed objects */
private StringBinding createStringBindingByBindingsAPI(
final ObjectBinding<TimeElapsed> elapsedBinding
) {
return Bindings.createStringBinding(
new Callable<String>() {
@Override
public String call() throws Exception {
return String.format(
"%.0f",elapsedBinding.getValue().getElapsed()
);
}
},elapsedBinding
);
}
/* @Return a new MediaView from a predefined MEDIA_PATH string */
private MediaView createMediaView() throws MalformedURLException {
String mediaURI = new File(MEDIA_PATH).toURI().toURL().toExternalForm();
Media media = new Media(mediaURI);
MediaPlayer player = new MediaPlayer(media);
MediaView mediaView = new MediaView(player);
player.play();
return mediaView;
}
/** immutable TimeElapsed class. */
class TimeElapsed {
private final double elapsed;
TimeElapsed(Duration duration) {
elapsed = duration.toSeconds();
}
public double getElapsed() {
return elapsed;
}
}
}
The above is only provided as a sample to adapt to the problem framework of using immutable objects in objectbinding, not the most effective way to track the progress of playing media
Alternative implementation
If I do not need to use immutable objects in objectbinding, I will directly monitor the progress attribute of mediaplayer, similar to the following code:
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.control.ProgressBar;
import javafx.scene.layout.StackPane;
import javafx.scene.media.*;
import javafx.stage.Stage;
import java.io.File;
import java.net.MalformedURLException;
public class MediaProgressMonitoring extends Application {
private static final String MEDIA_PATH = "C:\\Users\\Public\\Music\\Sample Music\\Dillon - Thirteen Thirtyfive.mp3";
public static void main(String[] args) { launch(args); }
@Override public void start(Stage stage) throws Exception {
final MediaView mediaView = createMediaView();
final MediaPlayer player = mediaView.getMediaPlayer();
final ProgressBar progress = new ProgressBar(0);
progress.setPrefWidth(800);
player.currentTimeproperty().addListener((observable) ->
progress.setProgress(
player.getCurrentTime().toMillis() /
player.getTotalDuration().toMillis()
)
);
StackPane layout = new StackPane();
layout.setStyle("-fx-background-color: cornsilk; -fx-padding: 20px;");
layout.getChildren().setAll(
mediaView,progress
);
stage.setScene(new Scene(layout));
stage.show();
}
private MediaView createMediaView() throws MalformedURLException {
String mediaURI = new File(MEDIA_PATH).toURI().toURL().toExternalForm();
Media media = new Media(mediaURI);
MediaPlayer player = new MediaPlayer(media);
MediaView mediaView = new MediaView(player);
player.play();
return mediaView;
}
}
