2016-11-21 91 views
1

我應該爲iOS和Android編寫一個應用程序,有時會在屏幕上顯示一個自定義的視頻播放器。我必須能夠控制它(尋找,播放,暫停,設置速度,選擇視頻...)。我知道這種媒體在Gluon中尚未得到支持。在膠子應用程序中嵌入原生視頻視圖

但是有可能在XCode和Android Studio中編寫這樣的東西,並以某種方式將其嵌入到Gluon應用程序中?

+0

我不確定這一點。據我所知,您可以在Android上編寫JavaFX-Fragments,因此將原生查看器Fragment與JavaFX混合在一起是可能的。但從表現來看...我不知道。和iOS ...根本不知道。也許你應該問一下,JavaFX的MediaPlayer API可以在這些平臺上做什麼。 – dzim

+0

謝謝dzim。我問的原因是我相信JavaFX media API在Android或iOS上無能爲力。 – Thorvaldur

+0

這似乎是正確的。根據這裏的問題(http://stackoverflow.com/questions/38419634/javafxports-how-to-call-android-native-media-player),你可以添加至少音頻功能,爲本地提供自己的界面播放器。但是視頻。我真的不確定。也許你應該相應地更新你的問題(標題中缺少「視頻」部分),也許膠子傢伙會試圖給你一個礦石告訴什麼是可能的,什麼不可能。但我仍然擔心,嵌入視頻播放器幾乎是不可能的...... – dzim

回答

1

繼Gluon Charm Down library的設計模式後,這可能是VideoService的基本Android實現。

正是基於這個tutorial,並且適於在該JavaFX的使用當前SurfaceView使用。它會創建一個TextureView,放置在當前視圖頂部的屏幕中央,佔用寬度的95%。

使用適用於您的IDE的Gluon插件,創建單個視圖項目。

  1. 廣場這兩個類在源代碼包,包com.gluonhq.charm.down.plugins

VideoService接口

package com.gluonhq.charm.down.plugins; 

public interface VideoService { 
    void play(String videoName); 
    void stop(); 
    void pause(); 
    void resume(); 
} 

VideoServiceFactory類

package com.gluonhq.charm.down.plugins; 

import com.gluonhq.charm.down.DefaultServiceFactory; 

public class VideoServiceFactory extends DefaultServiceFactory<VideoService> { 

    public VideoServiceFactory() { 
     super(VideoService.class); 
    } 

} 
  • Android Package的:將下的Android/Java程序包,包com.gluonhq.charm.down.plugins.android此類:
  • AndroidVideoService類

    package com.gluonhq.charm.down.plugins.android; 
    
    import android.content.Context; 
    import android.content.res.AssetFileDescriptor; 
    import android.graphics.SurfaceTexture; 
    import android.media.MediaMetadataRetriever; 
    import android.media.MediaPlayer; 
    import android.util.DisplayMetrics; 
    import android.util.Log; 
    import android.view.Surface; 
    import android.view.TextureView; 
    import android.view.WindowManager; 
    import android.widget.RelativeLayout; 
    import com.gluonhq.charm.down.plugins.VideoService; 
    import java.io.IOException; 
    import javafxports.android.FXActivity; 
    
    public class AndroidVideoService implements VideoService, TextureView.SurfaceTextureListener { 
        private static final String TAG = AndroidVideoService.class.getName(); 
        private MediaPlayer mMediaPlayer; 
        private String videoName; 
    
        private final RelativeLayout relativeLayout; 
        private final TextureView textureView; 
        private final DisplayMetrics displayMetrics; 
    
        public AndroidVideoService() { 
         displayMetrics = new DisplayMetrics(); 
         WindowManager windowManager = (WindowManager) FXActivity.getInstance().getSystemService(Context.WINDOW_SERVICE); 
         windowManager.getDefaultDisplay().getMetrics(displayMetrics); 
    
         relativeLayout = new RelativeLayout(FXActivity.getInstance()); 
    
         textureView = new TextureView(FXActivity.getInstance()); 
         textureView.setSurfaceTextureListener(this); 
         relativeLayout.addView(textureView); 
        } 
    
        @Override 
        public void play(String videoName) { 
         this.videoName = videoName; 
         stop(); 
         FXActivity.getInstance().runOnUiThread(() -> { 
          FXActivity.getViewGroup().addView(relativeLayout); 
         }); 
        } 
    
        @Override 
        public void stop() { 
         if (mMediaPlayer != null) { 
          mMediaPlayer.stop(); 
          mMediaPlayer.release(); 
          mMediaPlayer = null; 
         } 
         if (relativeLayout != null) { 
          FXActivity.getInstance().runOnUiThread(() -> { 
           FXActivity.getViewGroup().removeView(relativeLayout); 
          }); 
         } 
        } 
    
        @Override 
        public void pause() { 
         if (mMediaPlayer != null) { 
          mMediaPlayer.pause(); 
         } 
        } 
    
        @Override 
        public void resume() { 
         if (mMediaPlayer != null) { 
          mMediaPlayer.start(); 
         } 
        } 
    
        @Override 
        public void onSurfaceTextureAvailable(SurfaceTexture st, int i, int i1) { 
         Surface surface = new Surface(st); 
         try { 
          AssetFileDescriptor afd = FXActivity.getInstance().getAssets().openFd(videoName); 
          calculateVideoSize(afd); 
          mMediaPlayer = new MediaPlayer(); 
          mMediaPlayer.setDataSource(afd.getFileDescriptor(), afd.getStartOffset(), afd.getLength()); 
          mMediaPlayer.setSurface(surface); 
          mMediaPlayer.setLooping(true); 
          mMediaPlayer.prepareAsync(); 
          mMediaPlayer.setOnPreparedListener(mediaPlayer -> mediaPlayer.start()); 
    
         } catch (IllegalArgumentException | SecurityException | IllegalStateException | IOException e) { 
          Log.d(TAG, e.getMessage()); 
         } 
        } 
    
        @Override public void onSurfaceTextureSizeChanged(SurfaceTexture st, int i, int i1) { } 
        @Override public boolean onSurfaceTextureDestroyed(SurfaceTexture st) { return true; } 
        @Override public void onSurfaceTextureUpdated(SurfaceTexture st) { } 
    
        private void calculateVideoSize(AssetFileDescriptor afd) { 
         try { 
          MediaMetadataRetriever metaRetriever = new MediaMetadataRetriever(); 
          metaRetriever.setDataSource(afd.getFileDescriptor(), afd.getStartOffset(), afd.getLength()); 
          String height = metaRetriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_VIDEO_HEIGHT); 
          String width = metaRetriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_VIDEO_WIDTH); 
          double factor = Double.parseDouble(width) > 0 ? Double.parseDouble(height)/Double.parseDouble(width) : 1d; 
          // 95% screen width 
          RelativeLayout.LayoutParams lp = new RelativeLayout.LayoutParams((int) (0.95 * displayMetrics.widthPixels), 
            (int) (0.95 * displayMetrics.widthPixels * factor)); 
          lp.addRule(RelativeLayout.CENTER_IN_PARENT); 
          textureView.setLayoutParams(lp); 
         } catch (NumberFormatException e) { 
          Log.d(TAG, e.getMessage()); 
         } 
        } 
    } 
    
  • 樣品
  • 將在android /資產文件夾中的視頻文件,如big_buck_bunny.mp4,可從here下載。

    BasicView

    public class BasicView extends View { 
    
        private boolean paused; 
    
        public BasicView(String name) { 
         super(name); 
        } 
    
        @Override 
        protected void updateAppBar(AppBar appBar) { 
         appBar.setNavIcon(MaterialDesignIcon.MENU.button()); 
         appBar.setTitleText("Video View"); 
         // big_buck_bunny.mp4 video in src/android/assets: 
         Services.get(VideoService.class).ifPresent(video -> { 
          appBar.getActionItems().add(MaterialDesignIcon.PLAY_ARROW.button(e -> video.play("big_buck_bunny.mp4"))); 
          appBar.getActionItems().add(MaterialDesignIcon.PAUSE.button(e -> { 
           if (!paused) { 
            video.pause(); 
            paused = true; 
           } else { 
            video.resume(); 
            paused = false; 
           } 
          })); 
          appBar.getActionItems().add(MaterialDesignIcon.STOP.button(e -> video.stop())); 
         }); 
        } 
    
    } 
    
    你的Android設備和測試上

    部署:

    注意,TextureView將在頂部,直到你按下停止按鈕將其刪除。

    +0

    不錯!星期一會試試這個。早上的第一件事! – dzim

    +0

    這看起來很有希望。謝謝。當我再次被分配到這裏時,我一定會試試這個。 – Thorvaldur

    +0

    你認爲iOS實現也可以完成嗎?我在想的是,我需要類似於JNI後面Android的onSurfaceTexture方法來放置我的播放器。 – Thorvaldur

    0

    的本地視頻播放器(或在這種情況下,「預覽」的視頻的方法)在以下實施例中使用:

    https://gist.github.com/bgmf/d87a2bac0a5623f359637a3da334f980

    除了一些先決條件,代碼看起來是這樣的:

    package my.application; 
    
    import ch.cnlab.disentis.util.Constants; 
    import org.robovm.apple.foundation.*; 
    import org.robovm.apple.uikit.UIApplication; 
    import org.robovm.apple.uikit.UIDocumentInteractionController; 
    import org.robovm.apple.uikit.UIDocumentInteractionControllerDelegateAdapter; 
    import org.robovm.apple.uikit.UIViewController; 
    
    import java.io.File; 
    import java.util.logging.Logger; 
    
    public class NativeVideoServiceIOS extends PathHelperIOS implements NativeVideoService { 
        private static final Logger LOG = Logger.getLogger(NativeVideoServiceIOS.class.getName()); 
    
        public NativeVideoServiceIOS() { 
         LOG.warning("Initialized Native Video Service with path: " + this.pathBase); 
        } 
    
        @Override 
        public void triggerPlatformApp(String filename) { 
         String fullfile = pathBase.getAbsolutePath() + filename; 
         NSURL url = new NSURL(NSURLScheme.File, "", fullfile); 
         UIDocumentInteractionController popup = new UIDocumentInteractionController(url); 
         popup.setDelegate(new UIDocumentInteractionControllerDelegateAdapter() { 
          @Override 
          public UIViewController getViewControllerForPreview(UIDocumentInteractionController controller) { 
           return UIApplication.getSharedApplication() 
             .getWindows().first().getRootViewController(); 
          } 
         }); 
         popup.presentPreview(true); 
        } 
    
    }