基于原生MediaPlayer的自定义UI视频播放器控件开发

问题定义

最近在做视频播放器相关的开发,想要达到的一个目标是,利用原生的播放器组件实现在线视频的播放。在做了一番探索后,我完成了两套视频播放器的组件,均支持自定义播放界面的UI。其中一套是基于Android系统原生的MediaPlayer和VideoView,另一套是基于vitamio,这两套组件支持的视频格式是不相同的,而我做的工作是将其组件化,增加了可以支持自定义UI的特性。

原生VideoView实际上是将一个MediaPlayer和一个操作栏相结合,增加了一系列视频播放控制的操作而实现。其中,VideoView的视频控制栏的UI是Framework层实现的,用户无法进行自定义的布局。关于这一点可以参考这篇文章:Android Framework中的PolicyManager简介。因此,需要寻找一种办法,实现自定义UI的特性。

探索基础

首先第一步要做的就是将原生VideoView中利用Framework层实现的UI布局部分替换成自己的实现方案。在这一部分,我参考了这篇文章:

Custom Android media controller

通过以上这篇文章的步骤,我们可以得到一个通过布局文件生成的播放器控制栏。这样就为自定义的UI实现了可能。

我所做的封装和改进

我做的就是在上面这个库的基础上,将通过布局文件生成控制栏的功能进行了进一步的封装,改进成了一个比较容易、灵活的支持自定义UI的组件。我做了如下事情:

1. 将生成UI的过程从MeidaController中抽离出来

Chris的框架中,自定义UI的部分是在MediaController中生成的,并且该UI生成部分是和MediaControler的其他控制逻辑混在一起的,没有做到很好的解耦。我们在实际开发中,不可能专门重写这样一个完整的MediaController,尤其是在除UI部分以外其他视频控制功能上完全一致的前提下。于是我将这一部分抽离出来,只提供一个给MediaController进行UI绑定的接口出来。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
/**
* 公共接口:自定义控制条布局生成
*/
public interface MediaControllerGenerator {
/**
* 从布局文件生成一个控制条的自定义布局
* @return BaseMediaControllerHolder对象,控制条控件的集合
*/
BaseMediaControllerHolder generateMediaController();
}
private MediaControllerGenerator mUIGenerator;
public void setUIGenerator(MediaControllerGenerator generator) {
this.mUIGenerator = generator;
}

如上所示,我增加了一个MediaControllerGenerator接口,用来实现自定义UI的生成。我们只要在用到视频播放器的时候实现这个接口,通过generateMediaController()方法生成一个控制栏控件的集合,MediaController就可以自动将控制功能和自定义的UI布局实现绑定。

2. 封装了一个播放控制控件的集合

我们所开发的视频播放器,一把都会有开始暂停按钮、下一个视频、上一个视频、全屏、取消全屏等等的控制按钮。为了在自定义这些控件时更方便,我封装了一个自定义控件集合类BaseMediaControllerHolder,包含了所有可能用到的播放控件。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
/**
* VideoView的控制操作栏控件集合,可以根据自定义的UI布局对该对象进行赋值。
* 该控件集合继承了如下内容:
* {@link #pauseButton} 开始/暂停按钮
* {@link #startResId} 开始ICON资源ID
* {@link #pauseButton} 暂停ICON资源ID
* {@link #stopButton} 停止播放按钮
* {@link #totalTimeView} 视频总时间
* {@link #currentTimeView} 当前播放时间
* {@link #seekbar} 进度条
* {@link #titleView} 视频标题
* {@link #fullScreenButton} 全屏按钮
* {@link #fullscreenResId} 全屏ICON资源ID
* {@link #unfullscreenResId} 取消全屏ICON资源ID
* {@link #nextButton} 下一个按钮
* {@link #preButton} 上一个按钮
*
* Created by Anchorer on 2014/8/12.
*/
public class BaseMediaControllerHolder {
public View parentLayout; //父控件
public ImageButton pauseButton; //开始/暂停按钮
public ImageButton stopButton; //停止按钮
public TextView totalTimeView; //视频总长度
public TextView currentTimeView; //当前播放时间
public SeekBar seekbar; //进度条
public TextView titleView; //视频标题
public ImageButton fullScreenButton; //全屏按钮
public ImageButton nextButton; //下一个按钮
public ImageButton preButton; //上一个按钮
public ImageButton forwardButton; //快进按钮
public ImageButton backwardButton; //后退按钮
public ImageButton likeButton ; // 收藏
public ImageView imageViewBack ; //反悔按钮
public ImageView imageViewShare ;// 分享
public int startResId; //开始按钮图片资源ID
public int pauseResId; //暂停按钮图片资源ID
public int fullscreenResId; //全屏按钮图片资源ID
public int unfullscreenResId; //取消全屏按钮图片资源ID
private boolean hasStopped; //视频是否已停止,可能是播放结束,也可能是手动停止
private List<View> views; //支持一系列自定义的视图,该列表视图实现显示与隐藏
public BaseMediaControllerHolder() {}
...
}

当我们自定义MediaControllerGenerator接口的实现时,生成的也是这样一个控件集合类的对象。这样一来,我们所要做的工作就变成了,从自定义的xml布局文件中生成一系列控件,然后将这些控件封装在一起生成一个BaseMediaControllerHolder类,将这个类与MediaController绑定即可。

3. 在Activity中的应用

至此,我们已经实现了一个自定义UI的MediaController,接下来只要再实现MediaPlayer和该MediaController绑定就可以了,而这个绑定过程其实也就是原生MediaPlayer的使用方法,也就变得顺理成章了。该组件可以在我的Github上查看:

Anchorer/NativeVideoPlayerComponent

其中,MediaPlayer的其他必需的控制操作,我专门实现了一个BaseNativeVideoPlayerActivity的基类Activity,我们只要继承这个Activity实现自己的视频播放Activity即可。

关于该组件的具体使用方法,可以参考example中的Demo。

不足与改进

如果你做过基于Android原生视频播放器的相关开发,你就会了解,Android原生的视频播放器支持的视频格式是非常有限的。(可以参考:Supported Media Formats

为了对各种类型的视频做一些支持,需要在此基础上多做一些工作。实际上,这就是vitamio所做的。我完成的第二套播放器的组件,也是在vitamio的基础上,增加了自己实现的一个自定义UI的特性。