JavaFX多媒體(2)使用Media播放音訊與影像
JavaFX多媒體(1)使用Audio Clip播放音訊 << 前情 在前一篇當中,我們介紹如何以JavaFX的AudioClip類別播放簡短音訊,AudioClip類別的主要功能僅在於播放與停止播放音訊,並無法處理暫停播放、音訊時間長度、後設資料、等化器、事件等功能。 除了AudioClip類別之外,欲播放音訊與影像媒體,可使用以下類別處理:
其中Media類別用以設定音訊與影像來源,可為URL或本機檔案。此外,Media類別並處理音訊與影像的時間長度、後設資料、標記等資訊。 待Media類別設定音訊與影像來源之後,則交由MediaPlayer類別處理,如同其名稱Media Player (媒體播放器) 一般,用以播放、暫停、重覆或停止播放媒體,並處理調整音量平衡、重覆播放次數、播放速率、音量大小、時間長度、頻譜等資訊。 MediaView類別如同TableView、TreeView與WebView一般,用以「顯示」影像媒體。在實作上,若處理音訊,僅需使用Media與MediaPlayer類別;若處理影像,則需以Media、MediaPlayer與MediaView類別三者搭配一起使用。 請參考以下範例,分別以Media與MediaPlayer類別播放音訊,首先以Media類別設定音訊來源,可為URL或本機檔案,接著以MediaPlayer類別的setAutoPlay()方法自動播放音訊: Media media; MediaPlayer mediaplayer; ... try { String directory = System.getProperty("user.dir"); File file = new File(directory, "../../media/Audio.mp3"); media = new Media(file.toURI().toString()); mediaplayer = new MediaPlayer(media); // 自動播放音訊 mediaplayer.setAutoPlay(true); } catch (Exception ex) { ex.toString(); } 請參考以下範例,分別以Media、MediaPlayer與MediaView類別播放影像,首先以Media類別設定影像來源,可為URL或本機檔案,接著以MediaPlayer類別建立MediaPlayer物件,並由MediaView類別「顯示」影像媒體,最後再以MediaPlayer類別的play()方法播放影像: Media media; MediaPlayer mediaplayer; MediaView mediaview; ... try { String directory = System.getProperty("user.dir"); File file = new File(directory, "../../media/Video.mp4"); media = new Media(file.toURI().toString()); mediaplayer = new MediaPlayer(media); mediaview = new MediaView(mediaplayer); } catch (Exception ex) { ex.toString(); } ... BorderPane borderpane = new BorderPane(); borderpane.setCenter(mediaview); ... // 播放影像 mediaplayer.play(); 為免影像因尺寸太大而超出視窗範圍,因此以MediaView類別的setFitHeight()與setFitWidth()方法分別設定影像的適合 (Fit) 高度與寬度,以便讓影像符合視窗範圍。 此外,並以heightProperty().addListener()與widthProperty().addListener()方法分別處理視窗高度與寬度的改變事件,當視窗高度或寬度改變時,同時調整影像的高度或寬度: mediaview.setFitHeight(primaryStage.getHeight()); mediaview.setFitWidth(primaryStage.getWidth()); primaryStage.heightProperty().addListener(new ChangeListener() { @Override public void changed(ObservableValue observable, Number oldValue, Number newValue) { mediaview.setFitHeight((Double)newValue); } }); primaryStage.widthProperty().addListener(new ChangeListener() { @Override public void changed(ObservableValue observable, Number oldValue, Number newValue) { mediaview.setFitWidth((Double)newValue); } }); 請參考以下範例,範例加入標籤與滑動軸處理音訊,前者處理播放、暫停、向前與倒轉,後者調整時間長度、音量大小與音量平衡。 以播放與暫停為例,當點選播放標籤時,首先以MediaPlayer類別的getStatus()方法取得媒體目前的狀態,其回傳值分別為:
若狀態為正在播放時 (MediaPlayer.Status.PLAYING),則以MediaPlayer類別的pause()方法暫停播放;反之則以play()方法播放音訊: Label lblPlay = new Label(""); ... lblPlay.setOnMouseClicked(new EventHandler() { @Override public void handle(MouseEvent e) { // 取得媒體目前的狀態 if (mediaplayer.getStatus() == MediaPlayer.Status.PLAYING) { // 暫停播放 mediaplayer.pause(); } else { // 播放音訊 mediaplayer.play(); } } }); 以向前播放 (Forward) 為例,當點選向前標籤時,首先以MediaPlayer類別的getTotalDuration()方法取得音訊的總時間,接著以seek()方法移至音訊的最後,並藉此調整滑動軸的位置: Label lblForward = new Label(""); ... lblForward.setOnMouseClicked(new EventHandler() { @Override public void handle(MouseEvent e) { // 取得音訊的總時間 final Duration totalDuration = mediaplayer.getTotalDuration(); // 取得音訊目前的狀態 if (mediaplayer.getStatus() == MediaPlayer.Status.STOPPED) { mediaplayer.pause(); } // 移至音訊的最後 mediaplayer.seek(totalDuration); // 取得音訊目前的狀態 if (mediaplayer.getStatus() != MediaPlayer.Status.PLAYING) { if (sliderDuration.isValueChanging()) return; final Duration total = mediaplayer.getTotalDuration(); if (total == null || totalDuration == null) { // 調整滑動軸的位置 sliderDuration.setValue(0); } else { // 調整滑動軸的位置 sliderDuration.setValue( totalDuration.toMillis() / total.toMillis()); } } } }); 倒轉播放 (Rewind) 類似於向前播放,差別僅在於當點選倒轉標籤時,首先將音訊的目前進度時間設為Duration.ZERO (歸零),接著以seek()方法移至音訊的最前端,並藉此調整滑動軸的位置: Label lblRewind = new Label(""); ... lblRewind.setOnMouseClicked(new EventHandler() { @Override public void handle(MouseEvent e) { // 設定目前的進度時間為零 final Duration currentduration = Duration.ZERO; // 取得音訊目前的狀態 if (mediaplayer.getStatus() == MediaPlayer.Status.STOPPED) { mediaplayer.pause(); } // 移至音訊的最前端 mediaplayer.seek(currentduration); // 取得音訊目前的狀態 if (mediaplayer.getStatus() != MediaPlayer.Status.PLAYING) { if (sliderDuration.isValueChanging()) return; final Duration total = mediaplayer.getTotalDuration(); if (total == null) { // 調整滑動軸的位置 sliderDuration.setValue(0); } else { // 調整滑動軸的位置 sliderDuration.setValue( currentduration.toMillis() / total.toMillis()); } } } }); 以滑動軸調整音量大小 (Volume) 為例,首先以Slider類別的valueProperty()方法取得滑動軸的值屬性,接著以bindBidirectional()方法與MediaPlayer類別的volumeProperty()建立雙向繫結 (Bidirectional Binding),當移動滑動軸時,則以滑動軸的值設定音訊的音量大小: Slider sliderVolume = new Slider(); sliderVolume.setMin(0.0); sliderVolume.setMax(1.0); sliderVolume.setValue(1.0); sliderVolume.setPrefWidth(60); sliderVolume.setMaxWidth(Region.USE_PREF_SIZE); sliderVolume.setMinWidth(30); // 建立滑動軸與音訊音量的雙向繫結 sliderVolume.valueProperty().bindBidirectional(mediaplayer.volumeProperty()); 以滑動軸調整音量平衡 (Balance) 為例,首先以Slider類別的valueProperty()方法取得滑動軸的值屬性,接著以addListener()方法處理滑動軸事件,當移動滑動軸時,則以滑動軸的值設定音訊的音量平衡: final Slider sliderBalance = new Slider(); sliderBalance.setMin(-1.0); sliderBalance.setMax(1.0); sliderBalance.setValue(0.0); sliderBalance.setPrefWidth(60); sliderBalance.setMaxWidth(Region.USE_PREF_SIZE); sliderBalance.setMinWidth(30); sliderBalance.valueProperty().addListener(new InvalidationListener() { @Override public void invalidated(Observable observable) { if (sliderBalance.isValueChanging()) { // 設定音訊的音量平衡 mediaplayer.setBalance(sliderBalance.getValue()); } } }); 【執行結果】 請參考以下範例,加入標籤與滑動軸處理影像,前者處理播放、暫停、向前與倒轉,後者調整時間長度、音量大小與音量平衡,程式與前述範例幾乎一樣,差別僅在於,以MediaPlayer類別所建立MediaPlayer物件,需由MediaView類別「顯示」影像媒體。 【執行結果】 【參考資料】 [1] Java Official Web Site |