JiaoZiVideoPlayer使用說明(持續更新中...)

TrueloveSomeGIRl發表於2019-04-18

餃子視訊播放器使用說明

主要特點

  1. 可以完全自定義UI和任何功能
  2. 一行程式碼切換播放引擎,支援的視訊格式和協議取決於播放引擎,android.media.MediaPlayer ijkplayer
  3. 完美檢測列表滑動
  4. 可實現全屏播放,小窗播放
  5. 能在ListView、ViewPager和ListView、ViewPager和Fragment等多重巢狀模式下全屏工作
  6. 可以在載入、暫停、播放等各種狀態中正常進入全屏和退出全屏
  7. 多種視訊適配螢幕的方式,可鋪滿全屏,可以全屏剪裁
  8. 重力感應自動進入全屏
  9. 全屏後手勢修改進度和音量
  10. Home鍵退出介面暫停播放,返回介面繼續播放
  11. WebView巢狀本地視訊控制元件
  12. demo中新增視訊快取的例子

整合使用

即便是自定義UI,或者對Library有過修改,也是這五步驟來使用播放器。

1.新增類庫

compile 'cn.jzvd:jiaozivideoplayer:release'

2.新增布局
<cn.jzvd.JzvdStd
    android:id="@+id/videoplayer"
    android:layout_width="match_parent"
    android:layout_height="200dp"/>
複製程式碼
3.設定視訊地址、縮圖地址、標題
 MyJzvdStd myJzvdStd= findViewById(R.id.videoplayer);
 myJzvdStd.setUp("http://jzvd.nathen.cn/342a5f7ef6124a4a8faf00e738b8bee4/cf6d9db0bd4d41f59d09ea0a81e918fd-5287d2089db37e62345123a1be272f8b.mp4"
                , "餃子快長大", JzvdStd.SCREEN_WINDOW_NORMAL);
 Glide.with(this).load("http://jzvd-pic.nathen.cn/jzvd-pic/1bb2ebbe-140d-4e2e-abd2-9e7e564f71ac.png").into(myJzvdStd.thumbImageView);
複製程式碼
4.在Activity中
 @Override
    protected void onPause() {
        super.onPause();
        Jzvd.releaseAllVideos();
    }

    @Override
    public void onBackPressed() {
        if (Jzvd.backPress()) {
            return;
        }
        super.onBackPressed();
    }
複製程式碼
在AndroidManifest.xml中
<activity
    android:name=".MainActivity"
    android:configChanges="orientation|screenSize|keyboardHidden"
    android:screenOrientation="portrait" /> <!-- or android:screenOrientation="landscape"-->
複製程式碼

基本使用

加縮略載圖

Glide.with(this).load(Url).into(myJzvdStd.thumbImageView);   //推薦使用Glide
複製程式碼

自動播放

  自動播放有兩種 這裡隨便選擇新增一個,
1. myJzvdStd.startButton.performClick();
2. myJzvdStd.startVideo();
複製程式碼

跳轉制定位置播放

//這裡只有開始播放時才生效
mJzvdStd.seekToInAdvance = 20000;
//跳轉制定位置播放
JZMediaManager.seekTo(30000);
複製程式碼

2.播放sd卡下視訊

public void cpAssertVideoToLocalPath() {
        try {
            InputStream myInput;
            OutputStream myOutput = new FileOutputStream(Environment.getExternalStorageDirectory().getAbsolutePath() + "/DCIM/Camera/local_video.mp4");
            myInput = this.getAssets().open("local_video.mp4");
            byte[] buffer = new byte[1024];
            int length = myInput.read(buffer);
            while (length > 0) {
                myOutput.write(buffer, 0, length);
                length = myInput.read(buffer);
            }

            myOutput.flush();
            myInput.close();
            myOutput.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
myJzvdStd.setUp(Environment.getExternalStorageDirectory().getAbsolutePath() + "/DCIM/Camera/local_video.mp4", "餃子不信",Jzvd.SCREEN_WINDOW_NORMAL, );
這裡很多人問為什麼播不了,請認真怒url,播不了就是url沒怒對
複製程式碼

播放assets目錄下的視訊

       複製Demo中CustomMediaPlayerAssertFolder類到你的專案下
        ----------------------------------------------------------------------------
        JZDataSource jzDataSource = null;
        try {
            jzDataSource = new JZDataSource(getAssets().openFd("local_video.mp4"));
            jzDataSource.title = "餃子快長大";
        } catch (IOException e) {
            e.printStackTrace();
        }
        jzvdStd.setUp(jzDataSource, JzvdStd.SCREEN_WINDOW_NORMAL);
        Glide.with(this)
                .load("http://jzvd-pic.nathen.cn/jzvd-pic/1bb2ebbe-140d-4e2e-abd2-9e7e564f71ac.png")
                .into(jzvdStd.thumbImageView);

        Jzvd.setMediaInterface(new CustomMediaPlayerAssertFolder());//進入此頁面修改MediaInterface,讓此頁面的jzvd正常工作
複製程式碼

直接全屏播放

JzvdStd.startFullscreen(this, JzvdStd.class, VideoConstant.videoUrlList[6], "餃子辛苦了");
複製程式碼

開啟小窗播放

mJzvdStd.startWindowTiny();
複製程式碼

列表Item劃出開啟小窗播放

1.Listview
   listView.setOnScrollListener(new AbsListView.OnScrollListener() {
            @Override
            public void onScrollStateChanged(AbsListView view, int scrollState) { 

            }
            @Override
            public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) {
                Jzvd.onScrollAutoTiny(view, firstVisibleItem, visibleItemCount, totalItemCount);
              // Jzvd.onScrollReleaseAllVideos(view, firstVisibleItem, visibleItemCount, totalItemCount);  這是不開啟列表劃出小窗 同時也是畫出螢幕釋放JZ 劃出暫停
            }
        });
2. RecyclerView  劃出列表開啟小窗
   recyclerView.addOnChildAttachStateChangeListener(new RecyclerView.OnChildAttachStateChangeListener() {
            @Override
            public void onChildViewAttachedToWindow(View view) {
                Jzvd.onChildViewAttachedToWindow(view, R.id.videoplayer);
            }

            @Override
            public void onChildViewDetachedFromWindow(View view) {
                Jzvd.onChildViewDetachedFromWindow(view);
            }
        });
2.1 RecyclerView劃出螢幕釋放JZ,同時也是不開啟列表劃出顯示小窗
    recyclerView.addOnChildAttachStateChangeListener(new RecyclerView.OnChildAttachStateChangeListener() {
            @Override
            public void onChildViewAttachedToWindow(View view) {

            }

            @Override
            public void onChildViewDetachedFromWindow(View view) {
                Jzvd jzvd = view.findViewById(R.id.videoplayer);
                if (jzvd != null && jzvd.jzDataSource.containsTheUrl(JZMediaManager.getCurrentUrl())) {
                    Jzvd currentJzvd = JzvdMgr.getCurrentJzvd();
                    if (currentJzvd != null && currentJzvd.currentScreen != Jzvd.SCREEN_WINDOW_FULLSCREEN) {
                        Jzvd.releaseAllVideos();
                    }
                }
            }
        });
複製程式碼

小屏播放無聲音,全屏有聲音

建立一個類繼承JzvdStd並在XML設定
public class JzvdStdVolumeAfterFullscreen extends JzvdStd {
    public JzvdStdVolumeAfterFullscreen(Context context) {
        super(context);
    }

    public JzvdStdVolumeAfterFullscreen(Context context, AttributeSet attrs) {
        super(context, attrs);
    }

    @Override
    public void onPrepared() {
        super.onPrepared();
        if (currentScreen == SCREEN_WINDOW_FULLSCREEN) {
            JZMediaManager.instance().jzMediaInterface.setVolume(1f, 1f);
        } else {
            JZMediaManager.instance().jzMediaInterface.setVolume(0f, 0f);
        }
    }

    /**
     * 進入全屏模式的時候關閉靜音模式
     */
    @Override
    public void startWindowFullscreen() {
        super.startWindowFullscreen();
        JZMediaManager.instance().jzMediaInterface.setVolume(1f, 1f);
    }

    /**
     * 退出全屏模式的時候開啟靜音模式
     */
    @Override
    public void playOnThisJzvd() {
        super.playOnThisJzvd();
        JZMediaManager.instance().jzMediaInterface.setVolume(0f, 0f);
    }
}
複製程式碼

全屏狀態播放完成,不退出全屏

建立一個類繼承JzvdStd並在XML設定
public class JzvdStdAutoCompleteAfterFullscreen extends JzvdStd {
    public JzvdStdAutoCompleteAfterFullscreen(Context context) {
        super(context);
    }

    public JzvdStdAutoCompleteAfterFullscreen(Context context, AttributeSet attrs) {
        super(context, attrs);
    }

    @Override
    public void startVideo() {
        if (currentScreen == SCREEN_WINDOW_FULLSCREEN) {
            Log.d(TAG, "startVideo [" + this.hashCode() + "] ");
            initTextureView();
            addTextureView();
            AudioManager mAudioManager = (AudioManager) getContext().getSystemService(Context.AUDIO_SERVICE);
            mAudioManager.requestAudioFocus(onAudioFocusChangeListener, AudioManager.STREAM_MUSIC, AudioManager.AUDIOFOCUS_GAIN_TRANSIENT);
            JZUtils.scanForActivity(getContext()).getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);

            JZMediaManager.setDataSource(jzDataSource);
            JZMediaManager.instance().positionInList = positionInList;
            onStatePreparing();
        } else {
            super.startVideo();
        }
    }

    @Override
    public void onAutoCompletion() {
        if (currentScreen == SCREEN_WINDOW_FULLSCREEN) {
            onStateAutoComplete();
        } else {
            super.onAutoCompletion();
        }

    }
}
複製程式碼

全屏模式下顯示分享按鈕

複製DEMO下的layout檔案在 layout_top 佈局下 新增你的分享按鈕
public class JzvdStdShowShareButtonAfterFullscreen extends JzvdStd {

    public ImageView shareButton;

    public JzvdStdShowShareButtonAfterFullscreen(Context context) {
        super(context);
    }

    public JzvdStdShowShareButtonAfterFullscreen(Context context, AttributeSet attrs) {
        super(context, attrs);
    }

    @Override
    public void init(Context context) {
        super.init(context);
        shareButton = findViewById(R.id.share);
        shareButton.setOnClickListener(this);

    }

    @Override
    public int getLayoutId() {
        return R.layout.layout_standard_with_share_button;
    }

    @Override
    public void onClick(View v) {
        super.onClick(v);
        if (v.getId() == R.id.share) {
            Toast.makeText(getContext(), "Whatever the icon means", Toast.LENGTH_SHORT).show();
        }
    }

    @Override
    public void setUp(JZDataSource jzDataSource, int screen) {
        super.setUp(jzDataSource, screen);
        if (currentScreen == SCREEN_WINDOW_FULLSCREEN) {
            shareButton.setVisibility(View.VISIBLE);
        } else {
            shareButton.setVisibility(View.INVISIBLE);
        }
    }
}

複製程式碼

小屏狀態下不顯示標題,全屏模式下顯示標題

public class JzvdStdShowTitleAfterFullscreen extends JzvdStd {
    public JzvdStdShowTitleAfterFullscreen(Context context) {
        super(context);
    }

    public JzvdStdShowTitleAfterFullscreen(Context context, AttributeSet attrs) {
        super(context, attrs);
    }

    @Override
    public void setUp(JZDataSource jzDataSource, int screen) {
        super.setUp(jzDataSource, screen);
        if (currentScreen == SCREEN_WINDOW_FULLSCREEN) {
            titleTextView.setVisibility(View.VISIBLE);
        } else {
            titleTextView.setVisibility(View.INVISIBLE);
        }
    }
}
複製程式碼

播放MP3

public class JzvdStdMp3 extends JzvdStd {

    public JzvdStdMp3(Context context) {
        super(context);
    }

    public JzvdStdMp3(Context context, AttributeSet attrs) {
        super(context, attrs);
    }

    @Override
    public int getLayoutId() {
        return R.layout.jz_layout_standard_mp3;
    }

    @Override
    public void onClick(View v) {
        if (v.getId() == cn.jzvd.R.id.thumb &&
                (currentState == CURRENT_STATE_PLAYING ||
                        currentState == CURRENT_STATE_PAUSE)) {
            onClickUiToggle();
        } else if (v.getId() == R.id.fullscreen) {

        } else {
            super.onClick(v);
        }
    }

    //changeUiTo 真能能修改ui的方法
    @Override
    public void changeUiToNormal() {
        super.changeUiToNormal();
    }

    @Override
    public void changeUiToPreparing() {
        super.changeUiToPreparing();
    }

    @Override
    public void changeUiToPlayingShow() {
        super.changeUiToPlayingShow();
        thumbImageView.setVisibility(View.VISIBLE);

    }

    @Override
    public void changeUiToPlayingClear() {
        super.changeUiToPlayingClear();
        thumbImageView.setVisibility(View.VISIBLE);

    }

    @Override
    public void changeUiToPauseShow() {
        super.changeUiToPauseShow();
        thumbImageView.setVisibility(View.VISIBLE);

    }

    @Override
    public void changeUiToPauseClear() {
        super.changeUiToPauseClear();
        thumbImageView.setVisibility(View.VISIBLE);

    }

    @Override
    public void changeUiToComplete() {
        super.changeUiToComplete();
    }

    @Override
    public void changeUiToError() {
        super.changeUiToError();
    }
}

        jzvdStdMp3 = findViewById(R.id.jz_videoplayer_mp3);
        jzvdStdMp3.setUp(URL, "餃子搖擺", Jzvd.SCREEN_WINDOW_NORMAL);
        Glide.with(this)
                .load(VideoConstant.videoThumbs[0][1])
                .into(jzvdStdMp3.thumbImageView);
複製程式碼

播放完成不顯示預覽圖

public class JzvdStdShowTextureViewAfterAutoComplete extends JzvdStd {
    public JzvdStdShowTextureViewAfterAutoComplete(Context context) {
        super(context);
    }

    public JzvdStdShowTextureViewAfterAutoComplete(Context context, AttributeSet attrs) {
        super(context, attrs);
    }

    @Override
    public void onAutoCompletion() {
        super.onAutoCompletion();
        thumbImageView.setVisibility(View.GONE);
    }

}
複製程式碼

Home鍵退出介面暫停播放,返回介面繼續播放

  @Override
    protected void onResume() {
        super.onResume();
        //home back
        JzvdStd.goOnPlayOnResume();
    }

    @Override
    protected void onPause() {
        super.onPause();
   //     Jzvd.clearSavedProgress(this, null);
        //home back
        JzvdStd.goOnPlayOnPause();
    }
複製程式碼

邊播邊快取和清晰度切換

1. 整合videocache implementation 'com.danikula:videocache:2.7.0',並初始化
public class ApplicationDemo extends Application {

    @Override
    public void onCreate() {
        super.onCreate();
//        LeakCanary.install(this);
    }

    private HttpProxyCacheServer proxy;

    public static HttpProxyCacheServer getProxy(Context context) {
        ApplicationDemo app = (ApplicationDemo) context.getApplicationContext();
        return app.proxy == null ? (app.proxy = app.newProxy()) : app.proxy;
    }

    private HttpProxyCacheServer newProxy() {
        return new HttpProxyCacheServer(this);
    }
}
2.引用
        LinkedHashMap map = new LinkedHashMap();

        String proxyUrl = ApplicationDemo.getProxy(this).getProxyUrl(VideoConstant.videoUrls[0][9]);

        map.put("高清", proxyUrl);
        map.put("標清", VideoConstant.videoUrls[0][6]);
        map.put("普清", VideoConstant.videoUrlList[0]);
        JZDataSource jzDataSource = new JZDataSource(map, "餃子不信");
        jzDataSource.looping = true;
        jzDataSource.currentUrlIndex = 2;
        jzDataSource.headerMap.put("key", "value");//header
        mJzvdStd.setUp(jzDataSource
                , JzvdStd.SCREEN_WINDOW_NORMAL);
        Glide.with(this).load(VideoConstant.videoThumbList[0]).into(mJzvdStd.thumbImageView);
複製程式碼

重複播放

建立一個類整合JzvdStd並在XML設定
public class JZVideoPlayerStandardLoopVideo extends JzvdStd{
    public JZVideoPlayerStandardLoopVideo (Context context) {
        super(context);
    }

    public JZVideoPlayerStandardLoopVideo (Context context, AttributeSet attrs) {
        super(context, attrs);
    }

    @Override
    public void onAutoCompletion() {
        super.onAutoCompletion();
        startVideo();
    }
}
還有一種方法就是上面清晰度切換loop迴圈標誌
複製程式碼

重力感應自動進入全屏

    SensorManager mSensorManager = (SensorManager) getSystemService(SENSOR_SERVICE);
    Jzvd.JZAutoFullscreenListener mSensorEventListener = new Jzvd.JZAutoFullscreenListener();
    @Override
    protected void onResume() {
        super.onResume();
        Sensor accelerometerSensor = mSensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER);
        mSensorManager.registerListener(mSensorEventListener, accelerometerSensor, SensorManager.SENSOR_DELAY_NORMAL);
    }

    @Override
    protected void onPause() {
        super.onPause();
        mSensorManager.unregisterListener(mSensorEventListener);
   }

複製程式碼

重力感應

Jzvd.FULLSCREEN_ORIENTATION=ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE;
Jzvd.NORMAL_ORIENTATION = ActivityInfo.SCREEN_ORIENTATION_PORTRAIT;
兩個變數控制全屏前後的螢幕方向
複製程式碼

不儲存播放進度

Jzvd.SAVE_PROGRESS = false;
複製程式碼

取消播放時在非WIFIDialog提示

Jzvd.WIFI_TIP_DIALOG_SHOWED=true;
複製程式碼

清除某個URL進度

 Jzvd.clearSavedProgress(this, "url");
複製程式碼

切換播放核心

ijk
    複製Demo中JZMediaIjkplayer類到你的專案下 
    implementation 'tv.danmaku.ijk.media:ijkplayer-java:0.8.4'
    implementation 'tv.danmaku.ijk.media:ijkplayer-armv7a:0.8.4'
    Jzvd.setMediaInterface(new JZMediaIjkplayer());  //  ijkMediaPlayer
Mediaplayer
    Jzvd.setMediaInterface(new JZMediaSystem());  // 
exo
    複製Demo中JZExoPlayer類到你的專案下 
    implementation 'com.google.android.exoplayer:exoplayer:2.7.1'
    Jzvd.setMediaInterface(new JZExoPlayer());  //exo
複製程式碼

使用者埋點統計

    Jzvd.setJzUserAction(new MyUserActionStd());
    /**
     * 這只是給埋點統計使用者資料用的,不能寫和播放相關的邏輯,監聽事件請參考MyJzvdStd,複寫函式取得相應事件
     */
    class MyUserActionStd implements JZUserActionStd {

        @Override
        public void onEvent(int type, Object url, int screen, Object... objects) {
            switch (type) {
                case JZUserAction.ON_CLICK_START_ICON:
                    Log.i("USER_EVENT", "ON_CLICK_START_ICON" + " title is : " + (objects.length == 0 ? "" : objects[0]) + " url is : " + url + " screen is : " + screen);
                    break;
                case JZUserAction.ON_CLICK_START_ERROR:
                    Log.i("USER_EVENT", "ON_CLICK_START_ERROR" + " title is : " + (objects.length == 0 ? "" : objects[0]) + " url is : " + url + " screen is : " + screen);
                    break;
                case JZUserAction.ON_CLICK_START_AUTO_COMPLETE:
                    Log.i("USER_EVENT", "ON_CLICK_START_AUTO_COMPLETE" + " title is : " + (objects.length == 0 ? "" : objects[0]) + " url is : " + url + " screen is : " + screen);
                    break;
                case JZUserAction.ON_CLICK_PAUSE:
                    Log.i("USER_EVENT", "ON_CLICK_PAUSE" + " title is : " + (objects.length == 0 ? "" : objects[0]) + " url is : " + url + " screen is : " + screen);
                    break;
                case JZUserAction.ON_CLICK_RESUME:
                    Log.i("USER_EVENT", "ON_CLICK_RESUME" + " title is : " + (objects.length == 0 ? "" : objects[0]) + " url is : " + url + " screen is : " + screen);
                    break;
                case JZUserAction.ON_SEEK_POSITION:
                    Log.i("USER_EVENT", "ON_SEEK_POSITION" + " title is : " + (objects.length == 0 ? "" : objects[0]) + " url is : " + url + " screen is : " + screen);
                    break;
                case JZUserAction.ON_AUTO_COMPLETE:
                    Log.i("USER_EVENT", "ON_AUTO_COMPLETE" + " title is : " + (objects.length == 0 ? "" : objects[0]) + " url is : " + url + " screen is : " + screen);
                    break;
                case JZUserAction.ON_ENTER_FULLSCREEN:
                    Log.i("USER_EVENT", "ON_ENTER_FULLSCREEN" + " title is : " + (objects.length == 0 ? "" : objects[0]) + " url is : " + url + " screen is : " + screen);
                    break;
                case JZUserAction.ON_QUIT_FULLSCREEN:
                    Log.i("USER_EVENT", "ON_QUIT_FULLSCREEN" + " title is : " + (objects.length == 0 ? "" : objects[0]) + " url is : " + url + " screen is : " + screen);
                    break;
                case JZUserAction.ON_ENTER_TINYSCREEN:
                    Log.i("USER_EVENT", "ON_ENTER_TINYSCREEN" + " title is : " + (objects.length == 0 ? "" : objects[0]) + " url is : " + url + " screen is : " + screen);
                    break;
                case JZUserAction.ON_QUIT_TINYSCREEN:
                    Log.i("USER_EVENT", "ON_QUIT_TINYSCREEN" + " title is : " + (objects.length == 0 ? "" : objects[0]) + " url is : " + url + " screen is : " + screen);
                    break;
                case JZUserAction.ON_TOUCH_SCREEN_SEEK_VOLUME:
                    Log.i("USER_EVENT", "ON_TOUCH_SCREEN_SEEK_VOLUME" + " title is : " + (objects.length == 0 ? "" : objects[0]) + " url is : " + url + " screen is : " + screen);
                    break;
                case JZUserAction.ON_TOUCH_SCREEN_SEEK_POSITION:
                    Log.i("USER_EVENT", "ON_TOUCH_SCREEN_SEEK_POSITION" + " title is : " + (objects.length == 0 ? "" : objects[0]) + " url is : " + url + " screen is : " + screen);
                    break;

                case JZUserActionStd.ON_CLICK_START_THUMB:
                    Log.i("USER_EVENT", "ON_CLICK_START_THUMB" + " title is : " + (objects.length == 0 ? "" : objects[0]) + " url is : " + url + " screen is : " + screen);
                    break;
                case JZUserActionStd.ON_CLICK_BLANK:
                    Log.i("USER_EVENT", "ON_CLICK_BLANK" + " title is : " + (objects.length == 0 ? "" : objects[0]) + " url is : " + url + " screen is : " + screen);
                    break;
                default:
                    Log.i("USER_EVENT", "unknow");
                    break;
            }
        }
    }
複製程式碼

##相關函式回撥,螢幕狀態,播放器狀態,事件 #####繼承JzvdStd之後,可以通過父類的mCurrentState,取得當前的播放狀態。

  • CURRENT_STATE_IDLE 未知狀態,指控制元件被new出來之後什麼都沒做
  • CURRENT_STATE_NORMAL 普通狀態
  • CURRENT_STATE_PREPARING 視訊準備狀態
  • CURRENT_STATE_PREPARING_CHANGING_URL 播放中切換url的準備狀態
  • CURRENT_STATE_PLAYING 播放中狀態
  • CURRENT_STATE_PAUSE 暫停狀態
  • CURRENT_STATE_AUTO_COMPLETE 自動播放完成狀態
  • CURRENT_STATE_ERROR 錯誤狀態

複寫進入播放狀態的函式,取得播放狀態的回撥

  • onStateNormal 進入普通狀態,通常指setUp之後
  • onStatePreparing 進入準備中狀態,就是loading狀態
  • onStatePlaying 進入播放狀態
  • onStatePause 進入暫停狀態
  • onStateError 進入錯誤狀態
  • onStateAutoComplete 進入自動播放完成狀態
瞭解當前螢幕型別

全屏、小窗、非全屏分別是不同的例項,在繼承JzvdStd後,通過mCurrentScreen變數,取得當前螢幕型別

  • SCREEN_WINDOW_NORMAL 普通視窗(進入全屏之前的)
  • SCREEN_WINDOW_LIST 列表視窗(進入全屏之前)
  • SCREEN_WINDOW_FULLSCREEN 全屏
  • SCREEN_WINDOW_TINY 小窗 ####事件
  • 複寫onProgress函式,取得每次播放器設定底部seekBar的進度回撥
  • 呼叫changeUrl函式,切換url
  • 複寫onClick函式,取得各種按鈕的點選事件
  • 複寫onTouch函式,取得全屏之後的手勢操作

一些需求實現(以下實現都是本人的思考邏輯和借鑑一些網路,不完善,要根據你自己專案實際情況來)

1.會員試看功能 首先從思路上理清步驟

  • 首先拿到當前播放時間和是試看時間做判斷(考慮拖動進度條超過試看時間)
  • 從後臺拿到特定欄位判斷是否付費
  • 彈出試看結束功能Dialog
  • 付費完回來繼續播放(省去付費步驟)———取消
根據自己專案的實際情況來 ,這裡只是簡單的測試
public class TryToSeeVideo extends JzvdStd {
    RelativeLayout mRelativeLayout;
    TextView mExit, mPay, mTryToSee;
    boolean isNoPay;

    public TryToSeeVideo(Context context) {
        super(context);
    }

    public TryToSeeVideo(Context context, AttributeSet attrs) {
        super(context, attrs);
    }

    @Override
    public void init(Context context) {
        super.init(context);
        mRelativeLayout = findViewById(R.id.try_to_see_tip);
        mExit = findViewById(R.id.exit);   //退出按鈕
        mPay = findViewById(R.id.pay);     //支付按鈕
        mTryToSee = findViewById(R.id.try_see);    //重新試看按鈕
        mExit.setOnClickListener(this);
        mPay.setOnClickListener(this);
        mTryToSee.setOnClickListener(this);
    }

    @Override
    public int getLayoutId() {
        return R.layout.try_to_see_video_layout;
    }

    @Override
    public void onClick(View v) {
        super.onClick(v);
        switch (v.getId()) {
            case R.id.exit:
                //finish介面  建議回撥出當前事件
                break;
            case R.id.pay:
                //進入支付介面  支付成功後 Jzvd.goOnPlayOnResume();
                getContext().startActivity(new Intent(getContext(), ActivityMain.class));  //模擬跳轉支付介面
                break;
            case R.id.try_see:
                JZMediaManager.seekTo(0);//回到0秒
                Jzvd.goOnPlayOnResume();
                progressBar.setProgress(0);
                mRelativeLayout.setVisibility(GONE);
                break;
        }
    }

    /**
     * @param progress 百分比
     * @param position 當前時間
     * @param duration 總時長
     */
    @Override
    public void onProgress(int progress, long position, long duration) {
        super.onProgress(progress, position, duration);
        long totalSeconds = position / 1000;
        if (true) {   //這裡是從伺服器拿到特定欄位判斷是否付費  這裡模擬未付費
            if (totalSeconds >= 30) {   //考慮未付費情況下拖動進度條超過試看時間
                if (!isNoPay) {   //加個標記 因為此函式一直在回撥
                    JZMediaManager.seekTo(30000);   //回到30秒
                    progressBar.setProgress(30);
                    mRelativeLayout.setVisibility(VISIBLE);   //在適當的時候隱藏
                    Jzvd.goOnPlayOnPause();           //在適當的時候(支付成功後)播放
                    startButton.setEnabled(false);    //在適當的時候取消
                    isNoPay = true;
                }
            }
        }
    }

    @Override
    public void onStatePlaying() {
        super.onStatePlaying();
        isNoPay = false;
        startButton.setEnabled(true);
    }
}
複製程式碼

2.列表頁到詳情頁無時差播放 3.列表自動播放 4.自動播放下一集劇集

函式 changeUrl 三個過載 根據自己的實際情況來

  • changeUrl(String url, String title, long seekToInAdvance) //seekToInAdvance 時間
  • changeUrl(JZDataSource jzDataSource, long seekToInAdvance)
  • changeUrl(int urlMapIndex, long seekToInAdvance) 這裡是演示自動播放下一集(如果要自己選集,自定義UI定義出劇集列表,呼叫上面三個任意一個)
public class AutoPlayNextVideo extends JzvdStd {
    int UrlIndex = 0;  //劇集標識
    public AutoPlayNextVideo(Context context) {
        super(context);
    }

    public AutoPlayNextVideo(Context context, AttributeSet attrs) {
        super(context, attrs);
    }

    /**
     *    自動播放下一集
     *    若果需要手動選集 自定義Ui定義出劇集列表 呼叫相關changeUrl
     */
    @Override
    public void onAutoCompletion() {
        super.onAutoCompletion();
        if (UrlIndex<videoUrlList.length){
            UrlIndex++;
            changeUrl(videoUrlList[UrlIndex], "各劇集的標題", 0);
        }
    }
}
複製程式碼

倍速播放

public class JzvdStdSpeed extends JzvdStd {
    TextView tvSpeed;
    int currentSpeedIndex = 2;

    public JzvdStdSpeed(Context context) {
        super(context);
    }

    public JzvdStdSpeed(Context context, AttributeSet attrs) {
        super(context, attrs);
    }

    @Override
    public void init(Context context) {
        super.init(context);
        tvSpeed = findViewById(R.id.tv_speed);
        tvSpeed.setOnClickListener(this);
    }

    @Override
    public void setUp(JZDataSource jzDataSource, int screen) {
        super.setUp(jzDataSource, screen);
        if (currentScreen == SCREEN_WINDOW_FULLSCREEN) {
            tvSpeed.setVisibility(View.VISIBLE);
        } else {
            tvSpeed.setVisibility(View.GONE);
        }
        if (jzDataSource.objects == null) {
            Object[] object = {2};
            jzDataSource.objects = object;
            currentSpeedIndex = 2;
        } else {
            currentSpeedIndex = (int) jzDataSource.objects[0];
        }
        if (currentSpeedIndex == 2) {
            tvSpeed.setText("倍速");
        } else {
            tvSpeed.setText(getSpeedFromIndex(currentSpeedIndex) + "X");
        }
    }

    @Override
    public void onClick(View v) {
        super.onClick(v);
        if (v.getId() == R.id.tv_speed) {//0.5 0.75 1.0 1.25 1.5 1.75 2.0
            if (currentSpeedIndex == 6) {
                currentSpeedIndex = 0;
            } else {
                currentSpeedIndex += 1;
            }
            JZMediaManager.setSpeed(getSpeedFromIndex(currentSpeedIndex));
            tvSpeed.setText(getSpeedFromIndex(currentSpeedIndex) + "X");
            jzDataSource.objects[0] = currentSpeedIndex;
        }
    }

    @Override
    public int getLayoutId() {
        return R.layout.layout_std_speed;
    }

    private float getSpeedFromIndex(int index) {
        float ret = 0f;
        if (index == 0) {
            ret = 0.5f;
        } else if (index == 1) {
            ret = 0.75f;
        } else if (index == 2) {
            ret = 1.0f;
        } else if (index == 3) {
            ret = 1.25f;
        } else if (index == 4) {
            ret = 1.5f;
        } else if (index == 5) {
            ret = 1.75f;
        } else if (index == 6) {
            ret = 2.0f;
        }
        return ret;
    }
複製程式碼

##常見問題 視訊有聲音,無畫面

  • 開啟硬體加速

獲取視訊某一幀

/**
*  context 上下文
*  uri 視訊地址
*  imageView 設定image
*  frameTimeMicros 獲取某一時間幀
*/
public static void loadVideoScreenshot(final Context context, String uri, ImageView imageView, long frameTimeMicros) {
    RequestOptions requestOptions = RequestOptions.frameOf(frameTimeMicros);
    requestOptions.set(FRAME_OPTION, MediaMetadataRetriever.OPTION_CLOSEST);
    requestOptions.transform(new BitmapTransformation() {
        @Override
        protected Bitmap transform(@NonNull BitmapPool pool, @NonNull Bitmap toTransform, int outWidth, int outHeight) {
            return toTransform;
        }
        @Override
        public void updateDiskCacheKey(MessageDigest messageDigest) {
            try {
                messageDigest.update((context.getPackageName() + "RotateTransform").getBytes("utf-8"));
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    });
    Glide.with(context).load(uri).apply(requestOptions).into(imageView);
}
複製程式碼

視訊圓角

ViewOutlineProvider (注:該類是5.0的特性,低於5.0沒效果,如果要適配5.0以下這裡給出一個思路.9圖遮罩)
建立一個類繼承ViewOutLineProvider 實現改該方法

public class JzViewOutlineProvider extends ViewOutlineProvider {
    private float mRadius;
    public JzViewOutlineProvider(float radius) {
        this.mRadius = radius;
    }
    @Override
    public void getOutline(View view, Outline outline) {
       Rect rect = new Rect();
       view.getGlobalVisibleRect(rect);
       Rect selfRect = new Rect(0, 0,
             view.getWeight(),view.getHeight());
       outline.setRoundRect(selfRect,mRadius);
    }
}
  mJzvdStd.setOutlineProvider(new JzViewOutlineProvider(radius));
  mJzvdStd.setClipToOutline(true);

複製程式碼

點選全屏按鈕閃退,報錯空指標

  • 檢查是否正確按照基本使用在manifest中配置。Activity是否繼承自AppCompatActivity
 <activity
            android:name=".xxxxxxxActivity"
            android:configChanges="orientation|screenSize|keyboardHidden"
            android:screenOrientation="portrait" />
複製程式碼

視屏寬高不填充螢幕,有黑邊

  Jzvd.setTextureViewRotation(90);   //視訊旋轉90度

   根據自己情況選擇一個 填充
  Jzvd.setVideoImageDisplayType(Jzvd.VIDEO_IMAGE_DISPLAY_TYPE_FILL_PARENT); 
  Jzvd.setVideoImageDisplayType(Jzvd.VIDEO_IMAGE_DISPLAY_TYPE_FILL_SCROP);   

  Jzvd.setVideoImageDisplayType(Jzvd.VIDEO_IMAGE_DISPLAY_TYPE_ORIGINAL);  //原始大小
複製程式碼

背景高斯模糊

  • 以module的形式匯入專案,在jz_layout_standard.xml 的佈局下新增一個Imageview
 <FrameLayout
        android:id="@+id/surface_container"
        android:layout_width="match_parent"
        android:layout_height="match_parent">
        <cn.jzvd.ResizableImageView
            android:id="@+id/iv"
            android:layout_width="match_parent"
            android:layout_height="match_parent" />
    </FrameLayout>
這裡我為了方便重寫了Imageview 使他寬填充螢幕寬,這裡怎麼簡單怎麼來想進一切辦法讓他填充滿寬度
複製程式碼

在JzvdStd類下的init 方法裡面獲取上面的Imageview控制元件,好了到此就完了,然後在設定縮圖的下面用能高斯模糊的方法設定就OK,下面是我的做法你可以根據你自己來。

 myJzvdStd.setUp("http://jzvd.nathen.cn/342a5f7ef6124a4a8faf00e738b8bee4/cf6d9db0bd4d41f59d09ea0a81e918fd-5287d2089db37e62345123a1be272f8b.mp4"
                , "餃子快長大", JzvdStd.SCREEN_WINDOW_NORMAL);
        Glide.with(this).load("http://jzvd-pic.nathen.cn/jzvd-pic/1bb2ebbe-140d-4e2e-abd2-9e7e564f71ac.png").into(myJzvdStd.thumbImageView);
        Jzvd.setJzUserAction(new MyUserActionStd());
        Glide.with(ActivityMain.this)
                .load("http://t2.hddhhn.com/uploads/tu/201810/9999/9139710e12.jpg")
                .dontAnimate()
                // 設定高斯模糊
                .bitmapTransform(new BlurTransformation(this, 14, 5))
                .into(myJzvdStd.mFrameLayout);
複製程式碼

相關文章