如何使 MediaView 保持在我的 JavaFX MediaPlayer 应用程序中窗格的中心?

问题描述 投票:0回答:1

我正在使用 JavaFX (IntelliJ) 创建一个媒体播放器。我将 MediaView 放在 BorderPane 中心的 Pane(mediaPane) 内。我希望 mediaView 始终位于窗格的中心,同时保持其尺寸与窗格绑定。

这是我的 FXML 文件。

<?xml version="1.0" encoding="UTF-8"?>

<?import javafx.scene.control.Button?>
<?import javafx.scene.control.ComboBox?>
<?import javafx.scene.control.Label?>
<?import javafx.scene.control.Slider?>
<?import javafx.scene.layout.AnchorPane?>
<?import javafx.scene.layout.BorderPane?>
<?import javafx.scene.layout.HBox?>
<?import javafx.scene.layout.Pane?>
<?import javafx.scene.layout.StackPane?>
<?import javafx.scene.layout.VBox?>
<?import javafx.scene.media.MediaView?>
<?import javafx.scene.text.Font?>

<AnchorPane maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" prefHeight="557.0" prefWidth="846.0" xmlns="http://javafx.com/javafx/20.0.1" xmlns:fx="http://javafx.com/fxml/1" fx:controller="com.example.jmediaplayer.MediaPlayer">
   <children>
      <BorderPane fx:id="background" layoutX="193.0" layoutY="111.0" prefHeight="200.0" prefWidth="200.0" AnchorPane.bottomAnchor="0.0" AnchorPane.leftAnchor="0.0" AnchorPane.rightAnchor="0.0" AnchorPane.topAnchor="0.0">
         <bottom>
            <VBox fx:id="progressParent" prefHeight="129.0" prefWidth="846.0" style="-fx-background-color: #36454F;" BorderPane.alignment="CENTER">
               <children>
                  <Slider fx:id="progressSlider" />
                  <HBox fx:id="controlBar" alignment="CENTER" prefHeight="100.0" prefWidth="200.0" spacing="10.0">
                     <children>
                        <ComboBox fx:id="speedBox" onAction="#changeSpeed" prefHeight="56.0" prefWidth="150.0" promptText="speed" />
                        <Button fx:id="mediaPreviousButton" mnemonicParsing="false" onAction="#mediaPrevious" prefHeight="53.0" prefWidth="100.0" text="Previous" />
                        <Button fx:id="mediaResetButton" layoutX="72.0" layoutY="10.0" mnemonicParsing="false" onAction="#mediaReset" prefHeight="53.0" prefWidth="100.0" text="Reset" />
                        <Button fx:id="mediaActionButton" layoutX="134.0" layoutY="10.0" mnemonicParsing="false" onAction="#mediaAction" prefHeight="53.0" prefWidth="100.0" text="Play" />
                        <Button fx:id="mediaNextbutton" layoutX="196.0" layoutY="10.0" mnemonicParsing="false" onAction="#mediaNext" prefHeight="53.0" prefWidth="100.0" text="Next" />
                        <Slider fx:id="volumeBar" prefHeight="16.0" prefWidth="176.0" value="50.0" />
                     </children>
                  </HBox>
               </children>
            </VBox>
         </bottom>
         <top>
            <VBox fx:id="titleParent" prefHeight="100.0" prefWidth="846.0" style="-fx-background-color: #36454F;" BorderPane.alignment="CENTER">
               <children>
                  <Label fx:id="appTitle" alignment="CENTER" prefHeight="51.0" prefWidth="853.0" style="-fx-text-fill: #fff;" text="JMediaPlayer">
                     <font>
                        <Font name="System Bold Italic" size="42.0" />
                     </font>
                  </Label>
                  <HBox fx:id="mediaSelect" alignment="CENTER" prefHeight="46.0" prefWidth="846.0" spacing="10.0">
                     <children>
                        <Label fx:id="mediaTitle" prefHeight="16.0" prefWidth="705.0" style="-fx-text-fill: #fff;" text="Title">
                           <font>
                              <Font size="20.0" />
                           </font>
                        </Label>
                        <Button fx:id="openFileButton" mnemonicParsing="false" onAction="#selectFile" text="Open" />
                     </children>
                  </HBox>
               </children>
            </VBox>
         </top>
         <center>
            <VBox alignment="CENTER" prefHeight="100.0" prefWidth="200.0" BorderPane.alignment="CENTER" VBox.vgrow="ALWAYS">
               <children>
                  <StackPane alignment="CENTER" VBox.vgrow="ALWAYS">
                     <children>
                        <Pane fx:id="mediaPane" prefHeight="328.0" style="-fx-border-color: red; -fx-border-width: 2;" VBox.vgrow="ALWAYS">
                           <children>
                              <MediaView fx:id="mediaView" fitHeight="320.0" fitWidth="400.0" onMouseClicked="#mediaViewClicked" />
                           </children>
                        </Pane>
                     </children>
                  </StackPane>
               </children>
            </VBox>
         </center>
      </BorderPane>
   </children>
</AnchorPane>

这是我的控制器类

package com.example.jmediaplayer;

import static  com.example.jmediaplayer.constants.Constants.*;

import javafx.application.Platform;
import javafx.event.ActionEvent;
import javafx.fxml.FXML;
import javafx.fxml.Initializable;
import javafx.scene.control.*;
import javafx.scene.image.Image;
import javafx.scene.input.MouseEvent;
import javafx.scene.layout.*;
import javafx.scene.media.Media;
import javafx.scene.media.MediaView;
import javafx.stage.FileChooser;
import javafx.stage.FileChooser.ExtensionFilter;
import javafx.util.Duration;

import java.io.File;
import java.net.URL;
import java.util.Objects;
import java.util.ResourceBundle;

public class MediaPlayer implements Initializable {

    @FXML
    private BorderPane background;
    @FXML
    private Pane mediaPane;
    @FXML
    private Label mediaTitle, appTitle;
    @FXML
    private Button mediaActionButton, mediaNextbutton, mediaPreviousButton, mediaResetButton, openFileButton;
    @FXML
    private Slider progressSlider;
    @FXML
    private ComboBox<String> speedBox;
    @FXML
    private Slider volumeBar;
    @FXML
    private VBox progressParent, titleParent;
    @FXML
    private HBox controlBar, mediaSelect;
    @FXML
    private MediaView mediaView;

    private Media media;
    private javafx.scene.media.MediaPlayer mediaPlayer;
    private boolean isPlaying = false, isSeeking = false;
    private File file;
    private final String[] speeds = {"0.25x", "0.5x", "1x", "1.25x", "1.5x", "1.75x", "2x"};

    @Override
    public void initialize(URL url, ResourceBundle resourceBundle) {
        setLayout();

//        mediaActionButton.setGraphic(new FontIcon());
        configureProgressSlider();

        for (String speed : speeds) {
            speedBox.getItems().add(speed);
        }
        speedBox.setOnAction(this::changeSpeed);

        mediaView.setFocusTraversable(true);
        volumeBar.valueProperty().addListener((observableValue, number, t1) -> mediaPlayer.setVolume(volumeBar.getValue() * 0.01));
    }

    private void configureProgressSlider() {
        progressSlider.setMin(0);
        progressSlider.setMax(1);
        progressSlider.setValueChanging(true);
        progressSlider.addEventFilter(MouseEvent.MOUSE_PRESSED, e -> isSeeking = true);
        progressSlider.addEventFilter(MouseEvent.MOUSE_RELEASED, e -> isSeeking = false);

        progressSlider.valueProperty().addListener((observableValue, number, newValue) -> {
            if (isSeeking) {
                double newTime = newValue.doubleValue() * media.getDuration().toMillis();
                mediaPlayer.seek(Duration.millis(newTime));
            }
        });
    }

    void setLayout() {
        progressSlider.prefWidthProperty().bind(progressParent.widthProperty());

        controlBar.prefWidthProperty().bind(progressParent.widthProperty());

        appTitle.prefWidthProperty().bind(titleParent.widthProperty());
        mediaSelect.prefWidthProperty().bind(titleParent.widthProperty());

        mediaPane.prefWidthProperty().bind(background.widthProperty());
        mediaPane.prefHeightProperty().bind(background.heightProperty());

//        mediaView.setLayoutX((background.getWidth() - mediaView.getFitWidth()) / 2);
//        mediaView.setLayoutY((mediaPane.getHeight() - mediaView.getFitHeight()) / 2);
//        imagePane.prefWidthProperty().bind(background.widthProperty());
        mediaView.fitWidthProperty().bind(mediaPane.widthProperty());
        mediaView.fitHeightProperty().bind(mediaPane.heightProperty());
    }

    @FXML
    void changeSpeed(ActionEvent event) {
        if (speedBox.getValue() == null) {
            mediaPlayer.setRate(1);
        } else {
            mediaPlayer.setRate(Double.parseDouble(speedBox.getValue().substring(0, speedBox.getValue().length() - 1)));
        }
    }

    @FXML
    void mediaAction(ActionEvent event) {
        if (isPlaying) {
            mediaPlayer.pause();
            mediaActionButton.setText("Play");
        } else {
            changeSpeed(null);
            mediaPlayer.setVolume(volumeBar.getValue() * 0.01);
            mediaPlayer.play();
            mediaActionButton.setText("Pause");
        }
        isPlaying = !isPlaying;
    }

    @FXML
    void mediaNext(ActionEvent event) {

    }

    @FXML
    void mediaPrevious(ActionEvent event) {

    }

    @FXML
    void mediaReset(ActionEvent event) {
        if (mediaPlayer != null) {
            mediaPlayer.stop();
        }
    }

    @FXML
    void selectFile(ActionEvent event) {
        FileChooser fileChooser = new FileChooser();
        ExtensionFilter extensionFilter1 = new ExtensionFilter("MP4 files (*.mp4)", "*.mp4");
        ExtensionFilter extensionFilter2 = new ExtensionFilter("MP3 files (*.mp3)", "*.mp3");
        fileChooser.getExtensionFilters().add(extensionFilter1);
        fileChooser.getExtensionFilters().add(extensionFilter2);
        fileChooser.setSelectedExtensionFilter(extensionFilter1);
        file = fileChooser.showOpenDialog(null);

        setupMedia(file);
    }

    void setupMedia(File file) {
        if (file != null) {
            mediaReset(null);
            String extension = getFileExtension(file);
            mediaTitle.setText(file.getName());
            media = new Media(file.toURI().toString());
            mediaPlayer = new javafx.scene.media.MediaPlayer(media);
            mediaPlayer.currentTimeProperty().addListener((observableValue, duration, newTime) -> {
                double value = newTime.toMillis() / media.getDuration().toMillis();
                progressSlider.setValue(value);
            });

            if ("mp3".equalsIgnoreCase(extension)) {
                mediaView.setVisible(false);
//                imageView.setVisible(true);
                Image bg_for_mp3 = new Image(Objects.requireNonNull(getClass().getResource(IMAGE_PATH)).toExternalForm());
//                imageView.setImage(bg_for_mp3);
            } else if ("mp4".equalsIgnoreCase(extension)) {
//                imageView.setVisible(false);
                mediaView.setVisible(true);
                mediaView.setMediaPlayer(mediaPlayer);
            }
            if (isPlaying) {
                mediaAction(null);
            }
            progressSlider.setValue(0);
        }
    }

    @FXML
    void mediaViewClicked(MouseEvent event) {
//        System.out.println("Set Layout X : " + (background.getWidth() - mediaView.getFitWidth()) / 2);
//        System.out.println("Set Layout Y : " + (mediaPane.getHeight() - mediaView.getFitHeight()) / 2);
        Platform.runLater(() -> {
            System.out.println(background.getWidth() == mediaView.getFitWidth());
            System.out.println(mediaPane.getWidth() == mediaView.getFitWidth());
        });
    }

    String getFileExtension(File file) {
        int index = file.getName().lastIndexOf('.');
        if (index > 0) {
            return file.getName().substring(index+1);
        }
        return "";
    }
}

我尝试使用绑定属性将mediaPane与背景(BorderPane)绑定并将mediaView与mediaPane绑定,例如

mediaPane.prefWidthProperty().bind(background.widthProperty());
mediaPane.prefHeightProperty().bind(background.heightProperty());
mediaView.fitWidthProperty().bind(mediaPane.widthProperty());
mediaView.fitHeightProperty().bind(mediaPane.heightProperty());

它完成了使 mediaView 响应的工作,但它仍然只停留在 mediaPane 的左上角位置,因为它的layoutX和layoutY设置为0。 因此,我尝试手动将layoutX和layoutY设置为

mediaView.setLayoutX((background.getWidth() - mediaView.getFitWidth()) / 2);
mediaView.setLayoutY((mediaPane.getHeight() - mediaView.getFitHeight()) / 2);

但它没有做任何事情。我还尝试将

background.getWidth()
mediaView.getFitWidth()
mediaPane.getWidth()
的值打印在一起,似乎它们的值始终相等,因此
(background.getWidth() - mediaView.getFitWidth()) / 2
的值始终变为
0

然后我尝试删除 Pane 并将 mediaView 直接添加到 BorderPane 的中心,但是绑定 heightProperty 变得很困难,因为我的菜单位于顶部,控件位于底部。

我找不到任何其他解决方案。

java javafx fxml scenebuilder
1个回答
0
投票

创建自动调整大小

MediaView
,与创建自动调整大小
ImageView
类似。

尝试运行以下应用程序并调整窗口大小以查看效果。

import javafx.application.Application;
import javafx.beans.property.ObjectProperty;
import javafx.beans.property.SimpleObjectProperty;
import javafx.geometry.HPos;
import javafx.geometry.VPos;
import javafx.scene.Scene;
import javafx.scene.layout.Region;
import javafx.scene.media.Media;
import javafx.scene.media.MediaPlayer;
import javafx.scene.media.MediaView;
import javafx.stage.Stage;

public class MediaApp extends Application {

    public static final String MEDIA_URL = "https://download.samplelib.com/mp4/sample-5s.mp4";

    @Override
    public void start(Stage stage) {
        MediaPlayer mediaPlayer = new MediaPlayer(
                new Media(MEDIA_URL)
        );
        mediaPlayer.setCycleCount(MediaPlayer.INDEFINITE);
        MediaView mediaView = new MediaView(mediaPlayer);

        MediaViewPane mediaViewPane = new MediaViewPane(mediaView);
        mediaViewPane.setPrefSize(600, 400);

        stage.setScene(new Scene(mediaViewPane));
        stage.show();
    }

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

    class MediaViewPane extends Region {

        private ObjectProperty<MediaView> MediaViewProperty = new SimpleObjectProperty<>();

        public ObjectProperty<MediaView> MediaViewProperty() {
            return MediaViewProperty;
        }

        public MediaView getMediaView() {
            return MediaViewProperty.get();
        }

        public void setMediaView(MediaView MediaView) {
            this.MediaViewProperty.set(MediaView);
        }

        public MediaViewPane() {
            this(new MediaView());
        }

        @Override
        protected void layoutChildren() {
            MediaView mediaView = MediaViewProperty.get();
            if (mediaView != null) {
                mediaView.setFitWidth(getWidth());
                mediaView.setFitHeight(getHeight());
                layoutInArea(mediaView, 0, 0, getWidth(), getHeight(), 0, HPos.CENTER, VPos.CENTER);
            }
            super.layoutChildren();
        }

        public MediaViewPane(MediaView MediaView) {
            MediaViewProperty.addListener((arg0, oldMediaView, newMediaView) -> {
                if (oldMediaView != null) {
                    getChildren().remove(oldMediaView);
                }
                if (newMediaView != null) {
                    getChildren().add(newMediaView);
                }
            });
            this.MediaViewProperty.set(MediaView);
        }
    }
}

如果

MediaViewsetPreserveRatio
(默认),
true
将自动成为
信箱

© www.soinside.com 2019 - 2024. All rights reserved.