|
@@ -11,12 +11,14 @@
|
|
|
|
|
|
|
|
|
|
### 01.视频播放器UI封装需求
|
|
### 01.视频播放器UI封装需求
|
|
|
|
+- 发展中遇到的问题
|
|
|
|
+ - 播放器可支持多种场景下的播放,多个产品会用到同一个播放器,这样就会带来一个问题,一个播放业务播放器状态发生变化,其他播放业务必须同步更新播放状态,各个播放业务之间互相交叉,随着播放业务的增多,开发和维护成本会急剧增加, 导致后续开发不可持续。
|
|
- 播放器内核和UI层耦合
|
|
- 播放器内核和UI层耦合
|
|
- 也就是说视频player和ui操作柔和到了一起,尤其是两者之间的交互。比如播放中需要更新UI进度条,播放异常需要显示异常UI,都比较难处理播放器状态变化更新UI操作
|
|
- 也就是说视频player和ui操作柔和到了一起,尤其是两者之间的交互。比如播放中需要更新UI进度条,播放异常需要显示异常UI,都比较难处理播放器状态变化更新UI操作
|
|
- UI难以自定义或者修改麻烦
|
|
- UI难以自定义或者修改麻烦
|
|
- 比如常见的视频播放器,会把视频各种视图写到xml中,这种方式在后期代码会很大,而且改动一个小的布局,则会影响大。这样到后期往往只敢加代码,而不敢删除代码……
|
|
- 比如常见的视频播放器,会把视频各种视图写到xml中,这种方式在后期代码会很大,而且改动一个小的布局,则会影响大。这样到后期往往只敢加代码,而不敢删除代码……
|
|
- 有时候难以适应新的场景,比如添加一个播放广告,老师开课,或者视频引导业务需求,则需要到播放器中写一堆业务代码。迭代到后期,违背了开闭原则,视频播放器需要做到和业务分离
|
|
- 有时候难以适应新的场景,比如添加一个播放广告,老师开课,或者视频引导业务需求,则需要到播放器中写一堆业务代码。迭代到后期,违背了开闭原则,视频播放器需要做到和业务分离
|
|
-- 视频播放器结构不清晰
|
|
|
|
|
|
+- 视频播放器结构需要清晰
|
|
- 这个是指该视频播放器能否看了文档后快速上手,知道封装的大概流程。方便后期他人修改和维护,因此需要将视频播放器功能分离。比如切换内核+视频播放器(player+controller+view)
|
|
- 这个是指该视频播放器能否看了文档后快速上手,知道封装的大概流程。方便后期他人修改和维护,因此需要将视频播放器功能分离。比如切换内核+视频播放器(player+controller+view)
|
|
- 一定要解耦合
|
|
- 一定要解耦合
|
|
- 播放器player与视频UI解耦:支持添加自定义视频视图,比如支持添加自定义广告,新手引导,或者视频播放异常等视图,这个需要较强的拓展性
|
|
- 播放器player与视频UI解耦:支持添加自定义视频视图,比如支持添加自定义广告,新手引导,或者视频播放异常等视图,这个需要较强的拓展性
|
|
@@ -30,8 +32,25 @@
|
|
|
|
|
|
|
|
|
|
### 03.如何分离播放和UI分离
|
|
### 03.如何分离播放和UI分离
|
|
--
|
|
|
|
-
|
|
|
|
|
|
+- 方便播放业务发生变化
|
|
|
|
+ - 播放状态变化是导致不同播放业务场景之间交叉同步,解除播放业务对播放器的直接操控,采用接口监听进行解耦。比如:player+controller+interface
|
|
|
|
+- 关于视频播放器
|
|
|
|
+ - 定义一个视频播放器InterVideoPlayer接口,操作视频播放,暂停,缓冲,进度设置,设置播放模式等多种操作。
|
|
|
|
+ - 然后写一个播放器接口的具体实现类,在这个里面拿到内核播放器player,然后做相关的实现操作。
|
|
|
|
+- 关于视频视图View
|
|
|
|
+ - 定义一个视图InterVideoController接口,主要负责视图显示/隐藏,播放进度,锁屏,状态栏等操作。
|
|
|
|
+ - 然后写一个播放器视图接口的具体实现类,在这里里面inflate视图操作,然后接口方法实现,为了方便后期开发者自定义view,因此需要addView操作,将添加进来的视图用map集合装起来。
|
|
|
|
+- 播放器player和controller交互
|
|
|
|
+ - 在player中创建BaseVideoController对象,这个时候需要把controller添加到播放器中,这个时候有两个要点特别重要,需要把播放器状态监听,和播放模式监听传递给控制器
|
|
|
|
+ - setPlayState设置视频播放器播放逻辑状态,主要是播放缓冲,加载,播放中,暂停,错误,完成,异常,播放进度等多个状态,方便控制器做UI更新操作
|
|
|
|
+ - setPlayerState设置视频播放切换模式状态,主要是普通模式,小窗口模式,正常模式三种其中一种,方便控制器做UI更新
|
|
|
|
+- 播放器player和view交互
|
|
|
|
+ - 这块非常关键,举个例子,视频播放失败需要显示控制层的异常视图View;播放视频初始化需要显示loading,然后更新UI播放进度条等。都是播放器和视图层交互
|
|
|
|
+ - 可以定义一个类,同时实现InterVideoPlayer接口和InterVideoController接口,这个时候会重新这两个接口所有的方法。此类的目的是为了在InterControlView接口实现类中既能调用VideoPlayer的api又能调用BaseVideoController的api
|
|
|
|
+- 如何添加自定义播放器视图
|
|
|
|
+ - 添加了自定义播放器视图,比如添加视频广告,可以选择跳过,选择播放暂停。那这个视图view,肯定是需要操作player或者获取player的状态的。这个时候就需要暴露监听视频播放的状态接口监听
|
|
|
|
+ - 首先定义一个InterControlView接口,也就是说所有自定义视频视图view需要实现这个接口,该接口中的核心方法有:绑定视图到播放器,视图显示隐藏变化监听,播放状态监听,播放模式监听,进度监听,锁屏监听等
|
|
|
|
+ - 在BaseVideoController中的状态监听中,通过InterControlView接口对象就可以把播放器的状态传递到子类中
|
|
|
|
|
|
|
|
|
|
### 04.VideoPlayer如何实现
|
|
### 04.VideoPlayer如何实现
|
|
@@ -46,6 +65,94 @@
|
|
|
|
|
|
|
|
|
|
### 07.如何添加自定义播放视图
|
|
### 07.如何添加自定义播放视图
|
|
|
|
+- 比如,现在有个业务需求,需要在视频播放器刚开始添加一个广告视图,等待广告倒计时120秒后,直接进入播放视频逻辑。相信这个业务场景很常见,大家都碰到过,使用该播放器就特别简单,代码如下所示:
|
|
|
|
+- 首先创建一个自定义view,需要实现InterControlView接口,重写该接口中所有抽象方法,这里省略了很多代码,具体看demo。
|
|
|
|
+ ``` java
|
|
|
|
+ public class AdControlView extends FrameLayout implements InterControlView, View.OnClickListener {
|
|
|
|
+
|
|
|
|
+ private ControlWrapper mControlWrapper;
|
|
|
|
+ public AdControlView(@NonNull Context context) {
|
|
|
|
+ super(context);
|
|
|
|
+ init(context);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ private void init(Context context){
|
|
|
|
+ LayoutInflater.from(getContext()).inflate(R.layout.layout_ad_control_view, this, true);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ /**
|
|
|
|
+ * 播放状态
|
|
|
|
+ * -1 播放错误
|
|
|
|
+ * 0 播放未开始
|
|
|
|
+ * 1 播放准备中
|
|
|
|
+ * 2 播放准备就绪
|
|
|
|
+ * 3 正在播放
|
|
|
|
+ * 4 暂停播放
|
|
|
|
+ * 5 正在缓冲(播放器正在播放时,缓冲区数据不足,进行缓冲,缓冲区数据足够后恢复播放)
|
|
|
|
+ * 6 暂停缓冲(播放器正在播放时,缓冲区数据不足,进行缓冲,此时暂停播放器,继续缓冲,缓冲区数据足够后恢复暂停
|
|
|
|
+ * 7 播放完成
|
|
|
|
+ * 8 开始播放中止
|
|
|
|
+ * @param playState 播放状态,主要是指播放器的各种状态
|
|
|
|
+ */
|
|
|
|
+ @Override
|
|
|
|
+ public void onPlayStateChanged(int playState) {
|
|
|
|
+ switch (playState) {
|
|
|
|
+ case ConstantKeys.CurrentState.STATE_PLAYING:
|
|
|
|
+ mControlWrapper.startProgress();
|
|
|
|
+ mPlayButton.setSelected(true);
|
|
|
|
+ break;
|
|
|
|
+ case ConstantKeys.CurrentState.STATE_PAUSED:
|
|
|
|
+ mPlayButton.setSelected(false);
|
|
|
|
+ break;
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ /**
|
|
|
|
+ * 播放模式
|
|
|
|
+ * 普通模式,小窗口模式,正常模式三种其中一种
|
|
|
|
+ * MODE_NORMAL 普通模式
|
|
|
|
+ * MODE_FULL_SCREEN 全屏模式
|
|
|
|
+ * MODE_TINY_WINDOW 小屏模式
|
|
|
|
+ * @param playerState 播放模式
|
|
|
|
+ */
|
|
|
|
+ @Override
|
|
|
|
+ public void onPlayerStateChanged(int playerState) {
|
|
|
|
+ switch (playerState) {
|
|
|
|
+ case ConstantKeys.PlayMode.MODE_NORMAL:
|
|
|
|
+ mBack.setVisibility(GONE);
|
|
|
|
+ mFullScreen.setSelected(false);
|
|
|
|
+ break;
|
|
|
|
+ case ConstantKeys.PlayMode.MODE_FULL_SCREEN:
|
|
|
|
+ mBack.setVisibility(VISIBLE);
|
|
|
|
+ mFullScreen.setSelected(true);
|
|
|
|
+ break;
|
|
|
|
+ }
|
|
|
|
+ //暂未实现全面屏适配逻辑,需要你自己补全
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ ```
|
|
|
|
+- 然后该怎么使用这个自定义view呢?很简单,在之前基础上,通过控制器对象add进来即可,代码如下所示
|
|
|
|
+ ``` java
|
|
|
|
+ controller = new BasisVideoController(this);
|
|
|
|
+ AdControlView adControlView = new AdControlView(this);
|
|
|
|
+ adControlView.setListener(new AdControlView.AdControlListener() {
|
|
|
|
+ @Override
|
|
|
|
+ public void onAdClick() {
|
|
|
|
+ BaseToast.showRoundRectToast( "广告点击跳转");
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ @Override
|
|
|
|
+ public void onSkipAd() {
|
|
|
|
+ playVideo();
|
|
|
|
+ }
|
|
|
|
+ });
|
|
|
|
+ controller.addControlComponent(adControlView);
|
|
|
|
+ //设置控制器
|
|
|
|
+ mVideoPlayer.setController(controller);
|
|
|
|
+ mVideoPlayer.setUrl(proxyUrl);
|
|
|
|
+ mVideoPlayer.start();
|
|
|
|
+ ```
|
|
|
|
+
|
|
|
|
|
|
|
|
|
|
|
|
|