小九九 发表于 2024-7-2 16:57:53

Java写播放代码

本帖最后由 小九九 于 2024-7-2 16:58 编辑

package com.example.window;

import javafx.application.Application;
import javafx.application.Platform;
import javafx.beans.value.ChangeListener;
import javafx.beans.value.ObservableValue;
import javafx.geometry.Insets;
import javafx.geometry.Pos;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.Label;
import javafx.scene.control.ListView;
import javafx.scene.control.Slider;
import javafx.scene.layout.HBox;
import javafx.scene.layout.StackPane;
import javafx.scene.media.Media;
import javafx.scene.media.MediaPlayer;
import javafx.scene.media.MediaView;
import javafx.stage.FileChooser;
import javafx.stage.Stage;
import java.io.File;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;

public class VideoPlayerExample extends Application {
    private Stage primaryStage;
    private StackPane root;
    private MediaPlayer mediaPlayer;
    private ListView<String> playlist;
    private boolean isPlaylistVisible = true;
    private Button togglePlaylistButton;
    private Button openFileButton;
    private ScheduledExecutorService mouseIdleExecutor;
    private boolean isMouseIdle = false;

    // 提取文件名(不带扩展名)的方法
    private static String extractFileNameWithoutExtension(String path) {
      int lastSlashIndex = path.lastIndexOf('/');
      if (lastSlashIndex == -1) {
            lastSlashIndex = path.lastIndexOf('\\');
      }
      if (lastSlashIndex == -1) {
            return path;
      }
      String fileName = path.substring(lastSlashIndex + 1);
      int lastDotIndex = fileName.lastIndexOf('.');
      if (lastDotIndex == -1) {
            return fileName;
      }
      return fileName.substring(0, lastDotIndex);
    }

    @Override
    public void start(Stage primaryStage) {
      this.primaryStage = primaryStage;
      root = new StackPane();
      double borderWidth = 1.0;
      root.setStyle("-fx-background-color: black; -fx-border-color: black; -fx-border-width: " + borderWidth + "px;");

      // 默认视频文件
      File defaultFile = new File("G:/音乐.mp4");
      if (defaultFile.exists()) {
            Media media = new Media(defaultFile.toURI().toString());
            mediaPlayer = new MediaPlayer(media);
      } else {
            mediaPlayer = new MediaPlayer(new Media("https://www.example.com/default_video.mp4")); // 默认视频URL
      }
      mediaPlayer.setAutoPlay(true);

      MediaView mediaView = new MediaView(mediaPlayer);
      StackPane.setAlignment(mediaView, Pos.CENTER);
      mediaView.fitWidthProperty().bind(root.widthProperty().subtract(borderWidth * 2));
      mediaView.fitHeightProperty().bind(root.heightProperty().subtract(borderWidth * 2));

      // 播放列表
      playlist = new ListView<>();
      playlist.getItems().addAll(
            "",
            "节目 1",
            "节目 2",
            "节目 3",
            "节目 4",
            "节目 5",
            "节目 6",
            "节目 7",
            "节目 8",
            "节目 9",
            "节目 10"
      );
      playlist.setPrefWidth(200);
      playlist.setMaxWidth(200);
      playlist.setMinWidth(200);
      playlist.getSelectionModel().select(1);
      playlist.getStyleClass().add("playlist");

      // 播放列表显示/隐藏按钮
      togglePlaylistButton = new Button("\u003C");
      togglePlaylistButton.getStyleClass().add("togglePlaylistButton");
      togglePlaylistButton.setOnAction(event -> {
            isPlaylistVisible = !isPlaylistVisible;
            playlist.setVisible(isPlaylistVisible);
            openFileButton.setVisible(isPlaylistVisible);
            if (isPlaylistVisible) {
                togglePlaylistButton.setText("\u003C");
                togglePlaylistButton.setTranslateX(0);
                togglePlaylistButton.setVisible(true);
            } else {
                togglePlaylistButton.setText("\u003E");
                togglePlaylistButton.setTranslateX(-200);
                togglePlaylistButton.setVisible(false);
            }
      });

      HBox playlistContainer = new HBox(playlist, togglePlaylistButton);
      playlistContainer.setAlignment(Pos.CENTER_LEFT);
      HBox.setHgrow(playlist, javafx.scene.layout.Priority.ALWAYS);

      // 监听媒体播放状态
      mediaPlayer.statusProperty().addListener(new ChangeListener<MediaPlayer.Status>() {
            @Override
            public void changed(ObservableValue<? extends MediaPlayer.Status> observable, MediaPlayer.Status oldValue, MediaPlayer.Status newValue) {
                if (newValue == MediaPlayer.Status.PLAYING) {
                  String currentMediaPath = mediaPlayer.getMedia().getSource();
                  String currentMediaName = extractFileNameWithoutExtension(currentMediaPath);
                  updatePlaylist(currentMediaName);
                }
            }
      });

      StackPane.setAlignment(playlistContainer, Pos.CENTER_LEFT);
      root.getChildren().addAll(mediaView, playlistContainer);

      // 打开文件按钮
      openFileButton = new Button("文件");
      openFileButton.getStyleClass().add("openFileButton");
      openFileButton.setOnAction(event -> {
            FileChooser fileChooser = new FileChooser();
            fileChooser.getExtensionFilters().add(
                new FileChooser.ExtensionFilter("视频文件", "*.mp4", "*.mkv", "*.avi")
            );

            File selectedFile = fileChooser.showOpenDialog(primaryStage);
            if (selectedFile != null) {
                Media newMedia = new Media(selectedFile.toURI().toString());
                mediaPlayer.stop();
                mediaPlayer.dispose();
                mediaPlayer = new MediaPlayer(newMedia);
                mediaPlayer.setAutoPlay(true);
                mediaView.setMediaPlayer(mediaPlayer);
            }
      });

      StackPane.setAlignment(openFileButton, Pos.TOP_LEFT);
      root.getChildren().add(openFileButton);

      // 进度条
      Slider slider = new Slider();
      slider.setMin(0);
      slider.setMax(100);
      slider.setValue(0);
      slider.setShowTickLabels(false);
      slider.setShowTickMarks(false);
      slider.setMaxWidth(500);

      StackPane.setAlignment(slider, Pos.BOTTOM_CENTER);
      StackPane.setMargin(slider, new Insets(0, 0, 20, 0));

      slider.valueProperty().addListener((observable, oldValue, newValue) -> {
            double value = newValue.doubleValue();
            double max = slider.getMax();
            if (max != 0) { // 确保 max 不为 0,避免除以 0 的情况
                double fraction = value / max;
                if (!Double.isNaN(fraction)) { // 确保 fraction 不是 NaN
                  String color = String.format("-fx-background-color: linear-gradient(to right, #00ff00 %f%%, transparent %f%%);" +
                        "-fx-background-radius: 10px;", fraction * 100, fraction * 100);
                  slider.setStyle(color);
                } else {
                  // 处理 fraction 为 NaN 的情况,例如设置一个默认样式
                  slider.setStyle("-fx-background-color: linear-gradient(to right, #00ff00 0%, transparent 0%);" +
                        "-fx-background-radius: 10px;");
                }
            } else {
                // 处理 max 为 0 的情况,例如设置一个默认样式
                slider.setStyle("-fx-background-color: linear-gradient(to right, #00ff00 0%, transparent 0%);" +
                  "-fx-background-radius: 10px;");
            }
      });

      slider.setStyle("-fx-background-radius: 10px;");
      root.getChildren().add(slider);

      // 总时间标签
      Label durationLabel = new Label("00:00");
      durationLabel.getStyleClass().add("durationLabel");
      durationLabel.setTextFill(javafx.scene.paint.Color.RED);

      StackPane.setAlignment(durationLabel, Pos.BOTTOM_CENTER);
      StackPane.setMargin(durationLabel, new Insets(0, 0, 18.0, 0));
      durationLabel.translateXProperty().bind(slider.widthProperty().subtract(durationLabel.getWidth() / 2 + 135));
      root.getChildren().add(durationLabel);

      // 当前时间标签
      Label currentTimeLabel = new Label("00:00:00");
      currentTimeLabel.setTextFill(javafx.scene.paint.Color.WHITE);
      currentTimeLabel.getStyleClass().add("currentTimeLabel");

      StackPane.setAlignment(currentTimeLabel, Pos.BOTTOM_CENTER);
      StackPane.setMargin(currentTimeLabel, new Insets(0, 0, 17.5, 0));
      currentTimeLabel.translateXProperty().bind(slider.widthProperty().subtract(currentTimeLabel.getWidth() / 2 + 200));
      root.getChildren().add(currentTimeLabel);

      // 斜杠标签
      Label slashLabel = new Label("/");
      slashLabel.setTextFill(javafx.scene.paint.Color.WHITE);
      slashLabel.getStyleClass().add("slashLabel");

      StackPane.setAlignment(slashLabel, Pos.BOTTOM_CENTER);
      StackPane.setMargin(slashLabel, new Insets(0, 0, 18.0, 0));
      slashLabel.translateXProperty().bind(slider.widthProperty().subtract(slashLabel.getWidth() / 2 + 167.5));
      root.getChildren().add(slashLabel);

      // 播放/暂停按钮
      Button playButton = new Button();
      playButton.getStyleClass().add("playButton");
      playButton.setOnAction(event -> {
            if (mediaPlayer.getStatus() == MediaPlayer.Status.PLAYING) {
                mediaPlayer.pause();
                playButton.getStyleClass().remove("playButton");
                playButton.getStyleClass().add("pauseButton");
            } else {
                mediaPlayer.play();
                playButton.getStyleClass().remove("pauseButton");
                playButton.getStyleClass().add("playButton");
            }
      });

      StackPane.setAlignment(playButton, Pos.BOTTOM_CENTER);
      StackPane.setMargin(playButton, new Insets(0, 0, 45, 0));
      playButton.translateXProperty().bind(slider.widthProperty().subtract(playButton.getWidth() / 2 + 500));
      root.getChildren().add(playButton);

      // 设置视频总时间
      mediaPlayer.setOnReady(() -> {
            javafx.util.Duration totalDuration = mediaPlayer.getMedia().getDuration();
            int hours = (int) (totalDuration.toHours());
            int minutes = (int) (totalDuration.toMinutes() % 60);
            int seconds = (int) (totalDuration.toSeconds() % 60);
            durationLabel.setText(String.format("%d:%02d:%02d", hours, minutes, seconds));
      });

      // 视频结束时重置时间
      mediaPlayer.setOnEndOfMedia(() -> {
            durationLabel.setText("00:00:00");
      });

      // 更新当前播放时间
      mediaPlayer.currentTimeProperty().addListener((observable, oldValue, newValue) -> {
            javafx.util.Duration currentTime = newValue;
            int hours = (int) (currentTime.toHours());
            int minutes = (int) (currentTime.toMinutes() % 60);
            int seconds = (int) (currentTime.toSeconds() % 60);
            currentTimeLabel.setText(String.format("%d:%02d:%02d", hours, minutes, seconds));
      });

      final double THRESHOLD = 1;
      slider.valueProperty().addListener((observable, oldValue, newValue) -> {
            double value = newValue.doubleValue();
            double oldVal = oldValue.doubleValue();
            if (Math.abs(value - oldVal) >= THRESHOLD) {
                double max = slider.getMax();
                double fraction = value / max;
                javafx.util.Duration totalDuration = mediaPlayer.getMedia().getDuration();
                javafx.util.Duration newTime = new javafx.util.Duration(totalDuration.toMillis() * fraction);
                mediaPlayer.seek(newTime);
            }
      });

      mediaPlayer.currentTimeProperty().addListener((observable, oldValue, newValue) -> {
            javafx.util.Duration currentTime = newValue;
            javafx.util.Duration totalDuration = mediaPlayer.getMedia().getDuration();
            double fraction = currentTime.toMillis() / totalDuration.toMillis();
            slider.setValue(fraction * 100);
      });

      Scene scene = new Scene(root, 800, 600);
      scene.getStylesheets().add(getClass().getResource("/styles.css").toExternalForm());
      scene.setOnMouseClicked(event -> {
            if (event.getButton() == javafx.scene.input.MouseButton.PRIMARY && event.getClickCount() == 2) {
                toggleFullScreen();
            }
      });
      primaryStage.setScene(scene);
      primaryStage.setTitle("视频播放器");
      primaryStage.show();

      scene.setOnMouseMoved(event -> {
            resetMouseIdleTimer();
            if (!isPlaylistVisible) {
                togglePlaylistButton.setVisible(true);
            }
      });

      scene.setOnMouseExited(event -> {
            if (!isPlaylistVisible) {
                togglePlaylistButton.setVisible(false);
            }
      });

      initMouseIdleTimer();
      scene.setOnMouseMoved(event -> resetMouseIdleTimer());
      primaryStage.setScene(scene);
      primaryStage.setTitle("视频播放器");
      primaryStage.show();
    }

    // 初始化鼠标空闲计时器
    private void initMouseIdleTimer() {
      mouseIdleExecutor = Executors.newSingleThreadScheduledExecutor();
      scheduleMouseIdleTask();
    }

    // 重置鼠标空闲计时器
    private void resetMouseIdleTimer() {
      if (isMouseIdle) {
            Platform.runLater(() -> primaryStage.getScene().setCursor(javafx.scene.Cursor.DEFAULT));
      }
      isMouseIdle = false;
      mouseIdleExecutor.shutdownNow();
      mouseIdleExecutor = Executors.newSingleThreadScheduledExecutor();
      scheduleMouseIdleTask();
      if (!isPlaylistVisible) {
            togglePlaylistButton.setVisible(true);
      }
    }

    // 调度鼠标空闲任务
    private void scheduleMouseIdleTask() {
      mouseIdleExecutor.schedule(() -> {
            isMouseIdle = true;
            if (!isPlaylistVisible && togglePlaylistButton.getText().equals("\u003E")) {
                togglePlaylistButton.setVisible(false);
            }
            Platform.runLater(() -> primaryStage.getScene().setCursor(javafx.scene.Cursor.NONE));
      }, 5000, TimeUnit.MILLISECONDS);
    }

    // 更新播放列表
    private void updatePlaylist(String currentMediaName) {
      playlist.getItems().set(1, "正在播放: " + currentMediaName);
    }

    // 切换全屏
    private void toggleFullScreen() {
      primaryStage.setFullScreen(!primaryStage.isFullScreen());
    }

    public static void main(String[] args) {
      launch(args);
    }
}.playlist {
    -fx-background-color: rgba(0, 0, 0, 0.5); /* 设置背景颜色为半透明黑色 */
    -fx-text-fill: white;/* 设置文本颜色为白色 */
    -fx-control-inner-background: rgba(0, 0, 0, 0.5);/* 设置内部背景颜色为半透明黑色 */
    -fx-selection-bar: rgba(255, 255, 255, 0.3);/* 设置选择栏的颜色为半透明白色 */
    -fx-selection-bar-text: white;   /* 设置选择栏文本颜色为白色 */
}

.playlist .list-cell {
    -fx-font-size: 14px; /* 初始字体大小 */
}

.openFileButton {
    -fx-background-color: transparent;/* 设置背景颜色为透明 */
    -fx-text-fill: white;   /* 设置文本颜色为白色 */
    -fx-font-size: 14px;/* 设置字体大小为 14 像素 */
    -fx-font-weight: bold;   /* 设置字体加粗 */
}

.togglePlaylistButton {
    -fx-background-color: transparent;/* 设置背景颜色为透明 */
    -fx-text-fill: white;   /* 设置文本颜色为白色 */
    -fx-font-size: 20px;/* 设置字体大小为 20 像素 */
    -fx-font-weight: bold;   /* 设置字体加粗 */
    -fx-border-color: white;/* 设置边框颜色为白色 */
    -fx-border-width: 2px;/* 设置边框宽度为 2 像素 */
    -fx-border-radius: 50%;/* 设置边框圆角为 50% 以实现圆形 */
    -fx-background-radius: 50%;/* 设置背景圆角为 50% 以实现圆形 */
    -fx-padding: 5px;/* 设置内边距为 5 像素 */
}

.durationLabel {
    -fx-text-fill: white;   /* 设置文本颜色为白色 */
    -fx-font-size: 14px;    /* 设置字体大小为 14 像素 */
    -fx-font-weight: bold;/* 设置字体加粗 */
}

.currentTimeLabel {
    -fx-text-fill: white;   /* 设置文本颜色为白色 */
    -fx-font-size: 14px;    /* 设置字体大小为 14 像素 */
    -fx-font-weight: bold;/* 设置字体加粗 */
}

.slashLabel {
    -fx-text-fill: white;   /* 设置文本颜色为白色 */
    -fx-font-size: 14px;    /* 设置字体大小为 14 像素 */
    -fx-font-weight: bold;/* 设置字体加粗 */
}

.playButton {
    -fx-background-color: transparent;/* 设置背景颜色为透明 */
    -fx-text-fill: white;   /* 设置文本颜色为白色 */
    -fx-font-size: 16px;/* 设置字体大小为 16 像素 */
    -fx-font-weight: bold;   /* 设置字体加粗 */
    -fx-border-color: white;/* 设置边框颜色为白色 */
    -fx-border-width: 2px;/* 设置边框宽度为 2 像素 */
    -fx-padding: 3px;/* 设置内边距为 3 像素 */
    -fx-min-width: 25px;/* 设置最小宽度为 25 像素 */
    -fx-max-width: 25px;/* 设置最大宽度为 25 像素 */
    -fx-shape: "M 50,25 L 75,50 L 50,75 z";/* 设置形状为向右三角形 */
}

.pauseButton {
    -fx-background-color: transparent;/* 设置背景颜色为透明 */
    -fx-text-fill: white;   /* 设置文本颜色为白色 */
    -fx-font-size: 16px;/* 设置字体大小为 16 像素 */
    -fx-font-weight: bold;   /* 设置字体加粗 */
    -fx-border-color: white;/* 设置边框颜色为白色 */
    -fx-border-width: 2px;/* 设置边框宽度为 2 像素 */
    -fx-padding: 3px;/* 设置内边距为 3 像素 */
    -fx-min-width: 25px;/* 设置最小宽度为 25 像素 */
    -fx-max-width: 25px;/* 设置最大宽度为 25 像素 */
    -fx-shape: "M 40,25 L 50,25 L 50,75 L 40,75 z M 60,25 L 70,25 L 70,75 L 60,75 z";/* 设置形状为两条竖线 */
}

页: [1]
查看完整版本: Java写播放代码