大纲
* VideoView播放网络视频、播放本地视频
* 实现边播边缓存
* 获取视频的第一帧图片作为封面
* 实现类似朋友圈小视频循环播放
* 隐藏播放条
* 交互,点击屏幕退出播放
* 解决横屏生命周期重新加载的问题
* 加载时进度条的显示与消失监听 progressbar
videoview原型:
videoView extends SurfaceView implements MediaController.MediaPlayerControl
其中SurfaceView 为显示提供支持,MediaPlayerControl则为媒体控制提供了支持。
如果要构建更为复杂和有特色个性的视频View,需要继承SurfaceView 和实现MediaPlayerControl接口。
VideoView播放网络视频、播放本地视频
用videoview播放视频是非常简单的一件事,下面我放全代码,实现以下:
首先布局文件video_play_layout.xml中
<VideoView
android:id="@+id/videoPlayView"
android:layout_width="match_parent"
android:layout_height="240dp"
android:layout_centerVertical="true" />
VideoPlayActivity.java中
public class VideoPlayActivity extends AppCompatActivity {
private VideoView videoView;
private MediaController mc;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.video_play_layout);
videoView = (VideoView) findViewById(R.id.videoPlayView);
//设置视频控制器,组件可以控制视频的播放,暂停,快进,组件,不需要你实现
mc = new MediaController(this);
videoView.setMediaController(mc);
String netPlayUrl="http://baobab.wdjcdn.com/145076769089714.mp4";
Uri uri = Uri.parse(netPlayUrl);
videoView.setVideoURI(uri);//设置视频的播放地址,网络地址。播放网络视频
//播放本地视频
videoView.setVideoPath(Environment.getExternalStorageDirectory().getPath()+"video.mp4");
videoView.requestFocus();//让VideiView获取焦点
videoView.start();//开始播放
}
}
调用VideoView的如下两个方法来加载指定的视频
setVidePath(String path):加载路径文件代表的视频
setVideoURI(Uri uri):加载uri所对应的视频
⚠️:加载网络视频的时候记得要在AndroidManifest.xml中加入权限:
<uses-permission android:name="android.permission.INTERNET" />
实现边播边缓存
视频缓存策略:
先加载,缓存到本地再播放,这适用于微信朋友圈这种小视频、若视频过大,肯定不能用这种。
边播放边缓存:对余大多数,还是使用边播边缓存的。边播边缓存策略:
- 第一种方法:开两个线程,一个去正常让videoview请求播放,另一个线程去下载文件,等到再次点击播放,此时如果视频以及有缓存,就播放本地缓存。(但是这种相当于两份网络请求,时间会变慢)
- 第二种方法:通过代理的策略实现一个中间层将我们的网络请求转移到本地实现的代理服务器上,这样我们真正请求的数据就会被代理拿到,这样代理一边向本地写入数据,一边根据我们需要的数据看是读网络数据还是读本地缓存数据再提供给我们,真正做到了数据的复用。
对于videoview,github开源上有一个写好的库实现边播边缓存
地址:
AndroidVideoCache
AndroidVideoCache-视频边播放边缓存的代理策略
具体使用,是分简单,
compile 'com.danikula:videocache:2.7.0' //在app/build.gradle中加入依赖,引第三方
CustomViewApp.java
public class CustomViewApp extends Application {
//全局初始化一个本地代理服务器
private HttpProxyCacheServer proxy;
public static HttpProxyCacheServer getProxy(Context context) {
CustomViewApp app = (CustomViewApp) context.getApplicationContext();
return app.proxy == null ? (app.proxy = app.newProxy()) : app.proxy;
}
private HttpProxyCacheServer newProxy() {
return new HttpProxyCacheServer(this);
}
}
VideoPlayActivity.java中:
private VideoView videoView;
private MediaController mc;
private String proxyUrl;
private HttpProxyCacheServer proxy;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.video_play_layout);
videoView = (VideoView) findViewById(R.id.videoPlayView);
//设置视频控制器,组件可以控制视频的播放,暂停,快进,组件,不需要你实现
mc = new MediaController(this);
videoView.setMediaController(mc);
String netPlayUrl="http://baobab.wdjcdn.com/145076769089714.mp4";
proxy = CustomViewApp.getProxy(getApplicationContext());
proxyUrl = proxy.getProxyUrl(netPlayUrl);
videoView.setVideoPath(proxyUrl);//播放的是代理服务器返回的url,已经进行了封装处理
videoView.requestFocus();//让VideiView获取焦点
videoView.start();//开始播放
}
....
AndroidManifest.xml中,因为application改变了,所以application标签中,
android:name=".CustomViewApp" ⚠️
此时播放的时候,可以看到缓存的进度条,明显的边播边加载,在加载完完整的一次后,再次打开就不会加载了,而是播放缓存的视频。具体原理想要深层探究可以看我上面的文章链接
获取视频的第一帧图片作为封面
类似朋友圈,播放前我们是需要一个封面图加一个按钮,单击按钮跳转到我们刚才写的activity中,
* a .服务端提供url,直接提供一个方法获得url,并使用glide库将url转换成图片
* b.若服务端不提供url,MediaMetadataRetriever 类提供了一个统一的接口用于从一个输入媒体文件中取得帧和元数据。API官方链接:http://developer.android.com/reference/android/media/MediaMetadataRetriever.html,使用这个类去取得第一帧图片:
Bitmap bitmap = null;
MediaMetadataRetriever retriever = new MediaMetadataRetriever();
try {
//根据网络视频的url获取第一帧--亲测可用。但是这个方法获取本地视频的第一帧,不可用,还没找到方法解决。
if (Build.VERSION.SDK_INT >= 14) {
retriever.setDataSource(videoUrl, new HashMap<String, String>());
} else {
retriever.setDataSource(videoUrl);
}
//获得第一帧图片
bitmap = retriever.getFrameAtTime();
} catch (IllegalArgumentException e) {
e.printStackTrace();
} finally {
retriever.release();
}
return bitmap;
实现类似朋友圈小视频循环播放
videoview正常播放完毕会退出,如果做到循环播放,像朋友圈小视频一样?也是非常简单的。
//循环播放
videoView.setOnCompletionListener(new MediaPlayer.OnCompletionListener() {
@Override
public void onCompletion(MediaPlayer mediaPlayer) {
proxyUrl = proxy.getProxyUrl(playUrl);
videoView.setVideoPath(proxyUrl);
videoView.start();
}
});
隐藏播放条
去掉 private MediaController mc; 屏蔽关于它的操作
交互,点击屏幕退出播放
@Override
public boolean onTouchEvent(MotionEvent event) {
if (event.getAction()== MotionEvent.ACTION_UP){
finish();
}
return true;
}
解决横屏生命周期重新加载, 视频从头播放的问题
在默认情况下当萤幕从竖屏变到横屏时会触发onConfigurationChanged事件,画面会重新载入
解决:
- a. 在manifest中设置该Activity的configChanges为
android:configChanges=“screenSize|keyboardHidden|orientation” - b.重载onConfigurationChanged事件
解释:
- a.不设置Activity的android:configChanges时,切屏会重新调用各个生命周期,切横屏时会执行一次,切竖屏时会执行两次
- b.设置Activity的android:configChanges=“orientation"时,切屏还是会重新调用各个生命周期,切横、竖屏时只会执行一次
- c.设置Activity的android:configChanges=“orientation|keyboardHidden"时,切屏不会重新调用各个生命周期,只会执行onConfigurationChanged方法
- d.是由于google在android3.2中添加了screensize改变的通知,在转屏的时候,不仅是orientation发生了改变,screensize同样也发生了改变,所以要设置screenSize参数:
只竖屏显示的话(android:screenOrientation="portrait")
只横屏显示的话(android:screenOrientation="landscape")
加载时进度条的显示与消失 progressbar
视频最初加载的时候,如果什么处理都不做,网速又卡一点的话,会有几秒的黑屏,所以我们总要在加载的时候做一点处理,增加用户体验吧:可以直接放一个progressbar,或是生动一点,我是自定义了一个view,一个封面图加一个progressbar,加载的时候显示,加载完最初的一段就可以取消了。
videoView.setOnInfoListener(new MediaPlayer.OnInfoListener() {
@Override
public boolean onInfo(MediaPlayer mp, int what, int extra) {
if (what==MediaPlayer.MEDIA_INFO_BUFFERING_START){
bufferingCoverView.setVisibility(View.VISIBLE);
}
else {
bufferingCoverView.setVisibility(View.GONE);
}
return true;
}
});
//在视频预处理完成后调用
videoView.setOnPreparedListener(new MediaPlayer.OnPreparedListener() {
@Override
public void onPrepared(MediaPlayer mp) {
bufferingCoverView.setVisibility(View.GONE);
}
});
把之前写类似朋友圈播放视频时遇到的问题总结出来,哈哈哈,给个赞鼓励一下吧~~比心❤️