这篇主要介绍一些关于VideoView的更深层次的内容,上篇链接
1.调节视频音量
VideoView并没有提供调节视频音量的接口,如果要调节音量,可以有两种方式实现。
1.1 AudioManager实现音量的管理
Android系统分为三种音量类型,一是闹钟(Alarm),二是铃声(Ring),三是媒体(Media),如下图所示。
视频音量类型属于媒体(Media),如想要调节视频音量用AudioManager调用媒体类型的音量设置方法即可达到目的。
方法如下:
//第一步:获取AudioManager,context都有getSystemService方法
AudioManager audioManager = (AudioManager) getSystemService(Service.AUDIO_SERVICE);
//第二步:调用调节音量的方法
audioManager.adjustStreamVolume(AudioManager.STREAM_MUSIC, AudioManager.ADJUST_RAISE,
AudioManager.FLAG_PLAY_SOUND | AudioManager.FLAG_SHOW_UI);
参数说明:
- 参数1:
AudioManager.STREAM_MUSIC
,代表媒体类型的音量, 还有STREAM_RING
代表铃声类型,STREAM_ALARM
代表闹钟类型 - 参数2:
AudioManager.ADJUST_RAISE
表示增加音量,相应的ADJUST_LOWER
减少音量。 - 参数3:
AudioManager.FLAG_PLAY_SOUND | AudioManager.FLAG_SHOW_UI
,设置时的Flag,前者表示设置音量有声音提示,后者表示设置音量会弹出音量条界面。
上述方法只能每次增加或减少一格音量,无法做到设定特定的音量,如果想把媒体声音在静音与最大音量间互相切换,则需要调用AudioManager的另一个方法setStreamVolume(int streamType, int index, int flags)
,参数类型与上述加减音量的方法一致
。
示例如下:
//第一步:获取AudioManager对象
AudioManager audioManager = (AudioManager) getSystemService(Service.AUDIO_SERVICE);
//第二步:得到媒体的最大音量
int maxVolume = audioManager.getStreamMaxVolume(AudioManager.STREAM_MUSIC);
//第三步:获取媒体当前音量
int curVolume = audioManager.getStreamVolume(AudioManager.STREAM_MUSIC);
if (curVolume != maxVolume) {
//第四步:如果不是最大音量,则将媒体音量设置成最大音量
audioManager.setStreamVolume(AudioManager.STREAM_MUSIC, maxVolume, AudioManager.FLAG_PLAY_SOUND);
} else {
//第五步: 如果是最大音量,则将媒体音量设置成0,即静音
audioManager.setStreamVolume(AudioManager.STREAM_MUSIC, 0, AudioManager.FLAG_PLAY_SOUND);
}
三种类型的音量范围:
- AudioManager.STREAM_MUSIC:0~15
- AudioManager.STREAM_RING:0~7
- AudioManager.STREAM_ALARM:0~7
此方法的不足之外在于,它控制的是系统某个类型的音量,只要设置之后所有的媒体音量都是这样大小,可能会影响到其它程序的播放,如音乐。
1.2 持有一个MediaPlayer对象用于设置音量
MediaPlyaer有一个音量设置的API,可设置左右声道的输出音量,音量取值范围为0~1,0表示静音,1表示最大音量。
public void setVolume(float leftVolume, float rightVolume)
现在需要做的就是从VideoView中获取到一个MediaPlayer对象来调用此方法,有两种方式可以拿到一个MediaPlayer对象。
方法一:从播放回调中保存一个对象
最好在onInfoListener
中的回调中保存一个对象,因为这个回调只有在视频播放时才会触发,调节音量也是在有视频播放时才有意义。
//用成员变量保存一个MediaPlayer对象
private MediaPlayer mMediaPlayer;
//音量大小默认100%
private int mCurrentVolume = 100;
vvMain.setOnInfoListener(new MediaPlayer.OnInfoListener() {
@Override
public boolean onInfo(MediaPlayer mp, int what, int extra) {
//在视频播放信息回调中保存一个mediaplayer实例
mMediaPlayer = mp;
return false;
}
});
vvMain.setOnCompletionListener(new MediaPlayer.OnCompletionListener() {
@Override
public void onCompletion(MediaPlayer mp) {
//视频播放完毕如果不是设置的循环播放,则要将mMediaPlayer对象置空
if(mp.isLooping()){
LogUtils.dTag(TAG,"视频循环播放!");
}else {
mMediaPlayer = null;
}
}
vvMain.setOnErrorListener(new MediaPlayer.OnErrorListener() {
@Override
public boolean onError(MediaPlayer mp, int what, int extra) {
//当播放出现异常时,要记得将mediaplyaer置空,避免内存泄漏
mMediaPlayer = null;
//返回值设置为false,依然会弹窗提示错误,返回值为true,则不会弹,但也不会再调用onCompletion
return true;
}
});
//增加音量方法
public void increase(View view) {
//这里注意一定不要用mMediaPlayer.isPlay方法判断视频是否在播放,后很容易出现状态异常,当mMediaPlayer被释放再调用判断状态的方法就会报错
if(mMediaPlayer!=null && vvMain.isPlaying()){
if( mCurrentVolume == 100 ){
return;
}
mCurrentVolume+=10;
//音量的取值范围为0~1,小数数值,用整数加减再除以100的方式获得小数值是最合适的,如果用小数值进行加减很容易浮点数运算不精确
mMediaPlayer.setVolume(mCurrentVolume/100F,mCurrentVolume/100F);
LogUtils.dTag(TAG,"当前音量="+mCurrentVolume/100F);
}
}
//减少音量方法
public void decrease(View view) {
if(mMediaPlayer!=null && vvMain.isPlaying()){
if( mCurrentVolume == 0 ){
return;
}
mCurrentVolume-=10;
mMediaPlayer.setVolume(mCurrentVolume/100F,mCurrentVolume/100F);
LogUtils.dTag(TAG,"当前音量="+mCurrentVolume/100F);
}
}
@Override
protected void onPause() {
super.onPause();
vvMain.stopPlayback();
//当activity不在前台时置空
mMediaPlayer = null;
}
运用此方法设置视频的音量,一定要注意mMediaPlayer
对象的生命周期处理,不然容易出现较难察觉的内存泄漏。
方法二:运用反射获取VideoView中封装的MediaPlayer对象
VideoView中持有一个MediaPlayer对象,只要运用反射获取此对象的引用即可像上述方式一样设置视频的音量。
private MediaPlayer mMediaPlayer = null;
反射获取MediaPlayer对象方法
private MediaPlayer getReflectMediaPlayer(){
try {
Class clazz = Class.forName("android.widget.VideoView");
Field field = clazz.getDeclaredField("mMediaPlayer");
field.setAccessible(true);
return (MediaPlayer) field.get(vvMain);
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
接下来用反射获取的对象去设置音量即可,同方法一一致,用反射方法不用额外处理MediaPlayer对象的生命周期,在方法体中的对象用过即消失。反射的方式更加简洁,但会对性能有些影响。
2.VideoView在界面中的显示大小与视频源大小的关系
当我们在界面中放置VideoView控件时,会设置宽高属性,但视频源本身也是有一个宽高的,这两者有何关系呢?界面中显示的VideoView大小到底会如何显现,下面就用一个视频源大小与控件大小的关系表格来说明。
屏幕的宽高像素为: 1080*1920
视频源的宽高像素为: 1920*1080/1.78:1
屏幕是竖屏的,视频源是横屏的。
控件设置 | 显现的大小 |
---|---|
宽200dp,高200dp | 550*309/1.78:1 |
宽250,高200dp | 688*387/1.78:1 |
宽200,高250dp | 550*309/1.78:1< |