You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

JavaFX中MediaPlayer播放结束后添加随机延迟的实现方案

JavaFX MediaPlayer 播放完成后添加随机延迟并循环播放的实现

一、运行机制解释

  • JavaFX采用单线程UI模型:start()方法和所有UI操作(如窗口显示/隐藏)都必须在UI线程执行,这个线程负责处理所有UI渲染和用户交互。
  • MediaPlayer.play()是异步调用:调用后立即返回,实际的视频解码、播放由后台线程处理,但MediaPlayer的回调(比如setOnEndOfMedia)会被调度到UI线程执行。
  • 直接在player.play()后调用stage.hide():因为play()不阻塞UI线程,UI线程会立刻执行hide(),导致窗口还没开始播放就被隐藏。
  • 用TimeUnit.SECONDS.sleep():这会直接阻塞UI线程,整个JavaFX应用会卡住,视频播放、UI更新都无法进行,直到sleep结束才会继续执行player.play(),所以视频会延迟启动。

二、实现方案

核心思路:利用JavaFX原生的PauseTransition实现非阻塞的随机延迟,通过回调链完成「播放→结束隐藏→延迟→再次播放」的循环,完全适配JavaFX的线程模型,避免阻塞UI。

重构后的完整代码

import javafx.application.Application;
import javafx.beans.binding.Bindings;
import javafx.beans.property.DoubleProperty;
import javafx.scene.Scene;
import javafx.scene.layout.StackPane;
import javafx.scene.media.Media;
import javafx.scene.media.MediaPlayer;
import javafx.scene.media.MediaView;
import javafx.stage.Stage;
import javafx.scene.input.KeyCombination;
import javafx.util.Duration;

import java.io.File;

public class Main extends Application {
    private Stage stage;
    private final String[] videoPaths = {"lp.mp4", "sc_1.mp4"};
    private MediaView mediaView;
    private StackPane root;

    public static void main(String[] args) {
        launch(args);
    }

    private static int randomIntBetween(int first, int last) {
        return first + (int) (Math.random() * (last - first + 1));
    }

    @Override
    public void start(Stage stage) {
        this.stage = stage;
        // 窗口全局配置,仅初始化一次
        stage.setFullScreen(true);
        stage.setFullScreenExitHint("");
        stage.setFullScreenExitKeyCombination(KeyCombination.NO_MATCH);

        // 复用UI容器,避免重复创建组件
        mediaView = new MediaView();
        root = new StackPane(mediaView);

        // 绑定MediaView尺寸适配窗口
        DoubleProperty width = mediaView.fitWidthProperty();
        DoubleProperty height = mediaView.fitHeightProperty();
        width.bind(Bindings.selectDouble(mediaView.sceneProperty(), "width"));
        height.bind(Bindings.selectDouble(mediaView.sceneProperty(), "height"));
        mediaView.setPreserveRatio(true);

        Scene scene = new Scene(root);
        stage.setScene(scene);

        // 启动第一次播放流程
        playNextVideo();
    }

    private void playNextVideo() {
        // 随机选择视频
        String videoPath = videoPaths[randomIntBetween(0, videoPaths.length - 1)];
        File videoFile = new File(videoPath);
        Media media = new Media(videoFile.toURI().toString());
        MediaPlayer player = new MediaPlayer(media);

        // 播放完成后的回调逻辑
        player.setOnEndOfMedia(() -> {
            // 隐藏窗口
            stage.hide();
            // 生成3-100秒的随机延迟
            int delaySeconds = randomIntBetween(3, 100);
            PauseTransition delay = new PauseTransition(Duration.seconds(delaySeconds));
            // 延迟结束后启动下一次播放
            delay.setOnFinished(event -> playNextVideo());
            delay.play();
        });

        // 关联播放器与视图,显示窗口并开始播放
        mediaView.setMediaPlayer(player);
        stage.show();
        player.play();
    }
}

关键改进点

  • 移除了不必要的Runnable实现,改用Lambda处理播放完成回调,逻辑更清晰。
  • 窗口和UI容器仅初始化一次,避免重复创建Scene、StackPane等组件,提升运行效率。
  • 使用PauseTransition实现非阻塞延迟:它是JavaFX的动画类,后台计时结束后自动在UI线程执行回调,不会导致应用卡顿。
  • 通过playNextVideo()递归调用,实现无限循环播放,完全符合JavaFX的单线程模型要求。
  • 每次播放新视频时,创建新的MediaPlayer并关联到复用的MediaView,确保资源正确切换。

三、注意事项

  • 若视频文件较大,可在playNextVideo()开头添加旧MediaPlayer的资源释放逻辑:比如保留上一个播放器实例,调用player.dispose()避免内存泄漏。
  • 随机延迟范围可通过调整randomIntBetween(3, 100)的参数自定义。
  • 建议添加异常捕获,处理视频文件不存在或播放失败的情况。

内容的提问来源于stack exchange,提问作者Vova_Veider

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.24 22:19:58