8

通常のJavaプロジェクトでオーディオファイル(MP3 / Wav)を再生できる必要があります。JMFではなく新しいJavaFXMediaPlayerを使用したいと思います。私はこれをテストするためにいくつかのコードを書きました:

public void play()
{
    URL thing = getClass().getResource("mysound.wav");
    Media audioFile = new Media( thing.toString() );     
    try
    {                                       
        MediaPlayer player = new MediaPlayer(audioFile);
        player.play();
    }
    catch (Exception e)
    {
        System.out.println( e.getMessage() );
        System.exit(0);
    }        
}

これを実行すると、例外が発生します:ツールキットが初期化されていません

これはJavaFXスレッドと関係があると思います。私の質問は、どうすればこれを解決できますか?通常のアプリのバックグラウンドでオーディオファイルを再生するためだけにJavaFXパネルを作成する必要がありますか、それとも他の方法がありますか?

編集:スタックトレース:

java.lang.IllegalStateException: Toolkit not initialized
    at com.sun.javafx.application.PlatformImpl.runLater(PlatformImpl.java:121)
    at com.sun.javafx.application.PlatformImpl.runLater(PlatformImpl.java:116)
    at javafx.application.Platform.runLater(Platform.java:52)
    at javafx.scene.media.MediaPlayer.init(MediaPlayer.java:445)
    at javafx.scene.media.MediaPlayer.<init>(MediaPlayer.java:360)
    at javaapplication6.JavaApplication6.play(JavaApplication6.java:23)
    at javaapplication6.JavaApplication6.main(JavaApplication6.java:14)
4

1 に答える 1

17

Swing に JavaFX MediaPlayer を統合するソリューションの場合

JFXPanelを使用し、JavaFX システムが適切に初期化された後、JavaFX スレッドでのみ JavaFX オブジェクトを使用するように注意してください。

JavaFX は通常の Java であり、質問が少し混乱しますが、Swing のことだと思います。

Swing から起動するサンプル オーディオ プレーヤーを次に示します。このサンプルでは、​​Windows 7 の既定のパブリック サンプル ミュージック フォルダー (C:\Users\Public\Music\Sample Music) に多数の mp3 ファイルがあることを前提として、各ファイルを順番に再生します。

アプリのスクリーンショット

JavaFXMediaPlayerLaunchedFromSwing.java

このコードは、JavaFX ツールキットを初期化し、JavaFX アプリケーション スレッドで JavaFX シーンを作成する Swing アプリケーションの作成を担当します。

import javafx.application.Platform;
import javafx.embed.swing.JFXPanel;
import javafx.scene.Scene;

import javax.swing.*;

/**
 * Example of playing all mp3 audio files in a given directory
 * using a JavaFX MediaView launched from Swing
 */
public class JavaFXMediaPlayerLaunchedFromSwing {
    private static void initAndShowGUI() {
        // This method is invoked on Swing thread
        JFrame frame = new JFrame("FX");
        final JFXPanel fxPanel = new JFXPanel();
        frame.add(fxPanel);
        frame.setBounds(200, 100, 800, 250);
        frame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
        frame.setVisible(true);

        Platform.runLater(() -> initFX(fxPanel));
    }

    private static void initFX(JFXPanel fxPanel) {
        // This method is invoked on JavaFX thread
        Scene scene = new MediaSceneGenerator().createScene();
        fxPanel.setScene(scene);
    }

    public static void main(String[] args) {
        SwingUtilities.invokeLater(
            JavaFXMediaPlayerLaunchedFromSwing::initAndShowGUI
        );
    }
}

MediaSceneGenerator.java

.mp3指定されたフォルダー内のすべてのメディア ファイルを順番に再生する JavaFX メディア プレーヤーを作成します。メディアのいくつかのコントロール (再生、一時停止、トラックのスキップ、現在のトラックの再生進行状況インジケーター) を提供します。

import javafx.application.Platform;
import javafx.beans.value.ChangeListener;
import javafx.geometry.Pos;
import javafx.scene.Scene;
import javafx.scene.control.*;
import javafx.scene.layout.*;
import javafx.scene.layout.VBox;
import javafx.scene.media.*;
import javafx.util.Duration;

import java.io.File;
import java.util.*;

public class MediaSceneGenerator {
    private static final String MUSIC_FOLDER = "C:\\Users\\Public\\Music\\Sample Music";
    private static final String MUSIC_FILE_EXTENSION = ".mp3";

    private final Label currentlyPlaying = new Label();
    private final ProgressBar progress = new ProgressBar();
    private ChangeListener<Duration> progressChangeListener;

    public Scene createScene() {
        final StackPane layout = new StackPane();

        // determine the source directory for the playlist
        final File dir = new File(MUSIC_FOLDER);
        if (!dir.exists() || !dir.isDirectory()) {
            System.out.println("Cannot find media source directory: " + dir);
            Platform.exit();
            return null;
        }

        // create some media players.
        final List<MediaPlayer> players = new ArrayList<>();
        for (String file : Objects.requireNonNull(dir.list((dir1, name) -> name.endsWith(MUSIC_FILE_EXTENSION))))
            players.add(
                    createPlayer(
                            normalizeFileURL(dir, file)
                    )
            );
        if (players.isEmpty()) {
            System.out.println("No audio found in " + dir);
            Platform.exit();
            return null;
        }

        // create a view to show the mediaplayers.
        final MediaView mediaView = new MediaView(players.get(0));
        final Button skip = new Button("Skip");
        final Button play = new Button("Pause");

        // play each audio file in turn.
        for (int i = 0; i < players.size(); i++) {
            MediaPlayer player = players.get(i);
            MediaPlayer nextPlayer = players.get((i + 1) % players.size());
            player.setOnEndOfMedia(() -> {
                final MediaPlayer curPlayer = mediaView.getMediaPlayer();
                nextPlayer.seek(Duration.ZERO);
                if (nextPlayer != curPlayer) {
                    curPlayer.currentTimeProperty().removeListener(progressChangeListener);
                }
                mediaView.setMediaPlayer(nextPlayer);
                nextPlayer.play();
            });
        }

        // allow the user to skip a track.
        skip.setOnAction(actionEvent -> {
            final MediaPlayer curPlayer = mediaView.getMediaPlayer();
            MediaPlayer nextPlayer = players.get((players.indexOf(curPlayer) + 1) % players.size());
            nextPlayer.seek(Duration.ZERO);
            mediaView.setMediaPlayer(nextPlayer);
            if (nextPlayer != curPlayer) {
                curPlayer.currentTimeProperty().removeListener(progressChangeListener);
            }
            nextPlayer.play();
        });

        // allow the user to play or pause a track.
        play.setOnAction(actionEvent -> {
            if ("Pause".equals(play.getText())) {
                mediaView.getMediaPlayer().pause();
                play.setText("Play");
            } else {
                mediaView.getMediaPlayer().play();
                play.setText("Pause");
            }
        });

        // display the name of the currently playing track.
        mediaView.mediaPlayerProperty().addListener(
                (observableValue, oldPlayer, newPlayer) -> setCurrentlyPlaying(newPlayer)
        );

        // start playing the first track.
        mediaView.setMediaPlayer(players.get(0));
        mediaView.getMediaPlayer().play();
        setCurrentlyPlaying(mediaView.getMediaPlayer());

        // silly invisible button used as a template to get the actual preferred size of the Pause button.
        Button invisiblePause = new Button("Pause");
        invisiblePause.setVisible(false);
        play.prefHeightProperty().bind(invisiblePause.heightProperty());
        play.prefWidthProperty().bind(invisiblePause.widthProperty());

        // layout the scene.
        HBox controls = new HBox(10, skip, play, progress);
        controls.setAlignment(Pos.CENTER);
        VBox mediaPanel = new VBox(10, currentlyPlaying, mediaView, controls);

        layout.setStyle("-fx-background-color: cornsilk; -fx-font-size: 20; -fx-padding: 20; -fx-alignment: center;");
        layout.getChildren().addAll(
                invisiblePause,
                mediaPanel
        );
        progress.setMaxWidth(Double.MAX_VALUE);
        HBox.setHgrow(progress, Priority.ALWAYS);

        return new Scene(layout);
    }

    /**
     * sets the currently playing label to the label of the new media player and updates the progress monitor.
     */
    private void setCurrentlyPlaying(final MediaPlayer newPlayer) {
        progress.setProgress(0);
        progressChangeListener = (observableValue, oldValue, newValue) ->
                progress.setProgress(
                        1.0 * newPlayer.getCurrentTime().toMillis() / newPlayer.getTotalDuration().toMillis()
                );
        newPlayer.currentTimeProperty().addListener(progressChangeListener);

        String source = getUserFriendlyMediaName(newPlayer);
        currentlyPlaying.setText("Now Playing: " + source);
    }

    /**
     * @return a MediaPlayer for the given source which will report any errors it encounters
     */
    private MediaPlayer createPlayer(String aMediaSrc) {
        System.out.println("Creating player for: " + aMediaSrc);
        final MediaPlayer player = new MediaPlayer(new Media(aMediaSrc));
        player.setOnError(() -> System.out.println("Media error occurred: " + player.getError()));
        return player;
    }

    private String normalizeFileURL(File dir, String file) {
        return "file:///" + (dir + "\\" + file).replace("\\", "/").replaceAll(" ", "%20");
    }

    private String getUserFriendlyMediaName(MediaPlayer newPlayer) {
        String source = newPlayer.getMedia().getSource();

        source = source.substring(0, source.length() - MUSIC_FILE_EXTENSION.length());
        source = source.substring(source.lastIndexOf("/") + 1).replaceAll("%20", " ");

        return source;
    }
}

MediaPlayer を使用し、Swing を使用しないネイティブ JavaFX アプリケーションのみが必要な場合

Swing を使用する上記のソリューションは、尋ねられた質問に答えます。ただし、アプリケーションを埋め込む既存の Swing アプリケーションがない場合でも、この回答を利用して Java ベースのメディア プレーヤーを作成する人がいることに注意してください。

既存の Swing アプリケーションがない場合は、アプリケーションから Swing コードを完全に削除し、代わりにネイティブ JavaFX アプリケーションを記述します。これを行うには、上記のサンプルJavaFXMediaPlayerのクラスではなく、以下のクラスを使用します。JavaFXMediaPlayerLaunchedFromSwing

JavaFXMediaPlayer

import javafx.application.Application;
import javafx.stage.Stage;

public class JavaFXMediaPlayer extends Application {
    @Override
    public void start(Stage stage) throws Exception {
        stage.setScene(new MediaSceneGenerator().createScene());
        stage.show();
    }
}

フォローアップの質問への回答

Swing でビルドした .JAR ファイルには、netbeans のライブラリに追加すると、自動的に JavaFX が追加されますか?

注: パッケージに関するこのフォローアップの回答の情報は現在の日付である可能性が高く、現時点では他の推奨パッケージ オプションが存在します (例: https://github.com/openjfx/javafx-maven-plugin )。

技術的には、Swing は Jar ファイルをビルドしませんが、javafx パッケージ化コマンドの jar はビルドします。

アプリに JavaFX が含まれている場合は、JavaFX パッケージ ツールを使用することをお勧めします。それらがないと、Java ランタイム jar (jfxrt.jar) が jdk7u7 の Java ブート クラスパスに自動的に配置されないため、デプロイメントの問題が発生する可能性があります。ユーザーはランタイム クラスパスに手動で追加できますが、少し面倒かもしれません。将来の jdk バージョン (おそらく jdk7u10 または jdk8) では、jfxrt.jar がクラスパスに追加されます。それでも、JavaFX パッケージ化ツールの使用は、デプロイメント パッケージが最も互換性のある方法で動作することを保証する最良の方法であるため、引き続き推奨されます。

SwingInterop NetBeans プロジェクトは、JavaFX コンポーネントを埋め込む Swing プロジェクト用の JavaFX デプロイメント ツールを利用するサンプル NetBeans プロジェクトですSwingInterop のソースは、JDK 7 および JavaFX のデモとサンプルのダウンロードの一部です。

于 2012-09-23T01:00:22.607 に答える