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