java - 如何在 JavaFx 中创建自定义绑定(bind)类型

标签 java javafx

我有一个名为 TimeElapsed 的自定义类(它是不可变的)。 TimeElapsed 有一个接受 Duration 的构造函数(这是 JavaFx MediaPlayer 中用于跟踪时间的类型)。然后构造函数将 Duration 转换为 TimeElapsed

问题是我有一个函数需要返回 TimeElapsed ObservableValue。我需要的是能够做这样的事情:

new Binding<TimeElapsed>() {

     {
          super.bind(player.duration())
     }

     @Override
     protected TimeElapsed computeValue() {
          return new TimeElapsed(player.duration());
     }
}

但由于某些原因,没有Binding 泛型,您只能使用DoubleBinding 和类似的东西来实现,您可以在其中计算值但无法选择方式。那么,我该怎么办?

最佳答案

使用 ObjectBinding<TimeElapsed> 的示例解决方案.

按键方法

/* @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()
    );
}

完整的可执行示例

更改 MEDIA_PATH在示例中匹配您所需的媒体。

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

以上内容仅作为示例提供,以适应在 ObjectBinding 中使用不可变对象(immutable对象)的问题框架,而不是作为跟踪播放媒体进度的最有效方式。

替代实现

不需要在 ObjectBinding 中使用不可变对象(immutable对象),我会直接监视 MediaPlayer 的进度属性,类似于下面的代码:

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

关于java - 如何在 JavaFx 中创建自定义绑定(bind)类型,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/18213999/

相关文章:

java - 在 javaFX 中创建多个屏幕

java - 无法为 JDK11 构建 JemmyFX - 类 "com.sun.glass.ui.Robot"不再存在

JavaFX GridPane 单元格不会根据内容自动调整大小

JavaFx:组合框动态文本更改

java - 使用 Jackson 将 BigInteger 字段序列化为字符串(以简单的表示法表示)

java - 如何在AWS lambda函数执行期间在AWS S3存储桶中写入文件?

java - Groovy 中的系统调用失败

javascript - 为什么我的正则表达式在 JavaScript 中不起作用? (它适用于 Java)

java - 尝试在 eclipse 中安装 mase 插件时出现错误消息

java - 我的对象未打印到我的 JavaFX TableView