(一)VideoView
备注:
1、setMeasuredDimension()方法,决定了当前View的大小
2、MediaController 一个包含媒体播放器(MediaPlayer)控件的视图。包含了一些典型的按钮,像”播放(Play)/暂停(Pause)”, “倒带(Rewind)”, “快进(Fast Forward)”与进度滑动器(progress slider)。它管理媒体播放器(MediaController)的状态以保持控件的同步。
两个方法:同等效力,都是给cvvPlay控件添加MediaController
MediaController controller = new MediaController(this);
a、cvvPlay.setMediaController(controller);
b、controller.setMediaPlayer(cvvPlay);
参考自:http://www.cnblogs.com/over140/archive/2011/01/21/1940811.html
3、VideoView设置视频资源的方法:
setVideoURI();参数可以是本地资源也可以是网络资源
setViewPath();参数可以是本地资源路径也可以是网络路径
4、MediaMetadataRetriever类,能够从一个输入媒体文件中取得帧和元数据
参考自:http://blog.youkuaiyun.com/ameyume/article/details/7849641
// 生成视频资源的缩略图
private Bitmap createBitmapFromUrl(String url){
Bitmap bitmap = null;
// 构建MediaMetadataRetriever对象,该对象能够从一个输入媒体文件中取得帧和元数据
MediaMetadataRetriever retriever= new MediaMetadataRetriever();;
try {
// 给MediaMetadataRetriever对象设置数据源
retriever.setDataSource(this,Uri.parse(url));
// 通过MediaMetadataRetriever对象的getFrameAtTime()方法获取任意位置的帧的图片资源
// 注意:该方法一定要放在setDataSource()之后调用
bitmap = retriever.getFrameAtTime();
}catch (Exception e){
e.printStackTrace();
}finally {
// 释放资源
retriever.release();
}
(二)SurfaceView
1、对比:View绘图机制的缺陷:
View缺乏双缓冲机制;
当程序需要更新View上的图像时,程序必须重绘View上显示的整张图片;
新线程无法直接更新View组件。
View存在上述缺陷,所以通过自定义View来绘图,尤其是游戏中的绘图,性能不好。一般推荐使用SurfaceView。
SurfaceView有以下三个特点:
A. 具有独立的绘图表面;
B. 需要在宿主窗口上挖一个洞来显示自己;
C. 它的UI绘制可以在独立的线程中进行,这样就可以进行复杂的UI绘制,并且不会影响应用程序的主线程响应用户输入。
2、使用SurfaceView的必要性:
普通的Android控件,例如TextView、Button和CheckBox等,它们都是将自己的UI绘制在宿主窗口的绘图表面之上,这意味着它们的UI是在应用程序的主线程中进行绘制的。由于应用程序的主线程除了要绘制UI之外,还需要及时地响应用户输入,否则的话,系统就会认为应用程序没有响应了,因此就会弹出一个ANR对话框出来。对于一些游戏画面,或者摄像头预览、视频播放来说,它们的UI都比较复杂,而且要求能够进行高效的绘制,因此,它们的UI就不适合在应用程序的主线程中进行绘制。这时候就必须要给那些需要复杂而高效UI的视图生成一个独立的绘图表面,以及使用一个独立的线程来绘制这些视图的UI。
3、实现SurfaceView:
a、首先继承SurfaceView并实现SurfaceHolder.Callback接口:
使用接口的原因:因为使用SurfaceView 有一个原则,所有的绘图工作必须得在Surface 被创建之后才能开始,而在Surface 被销毁之前必须结束。所以Callback 中的surfaceCreated() 和surfaceDestroyed() 就成了绘图处理代码的边界。
b、SurfaceHolder.Callback接口需要重写的方法:
(1)public void surfaceChanged(SurfaceHolder holder,int format,int width,int height){}
在surface的大小发生改变时激发
(2)public void surfaceCreated(SurfaceHolder holder){}
在创建时激发,一般在这里调用画图的线程。
(3)public void surfaceDestroyed(SurfaceHolder holder) {}
销毁时激发,一般在这里将画图的线程停止、释放。
c、几个需要注意的方法:
整个过程:【重要】
SurfaceView.getHolder()获得SurfaceHolder对象 —->
SurfaceHolder.addCallback(callback)添加回调方法—->
实现SurfaceHolder.Callback接口 ,重写其中的方法—->
SurfaceHolder.lockCanvas()获得Canvas对象并锁定画布—->(在surfaceCreated()方法中执行)
Canvas绘画 —->(在surfaceCreated()方法中执行)
SurfaceHolder.unlockCanvasAndPost(Canvas canvas)结束锁定画图,并提交改变,将图形显示。(在surfaceCreated()方法中执行)
案例一:
在suifaceCreated方法中获得一个画布对象,并在该方法中绘制图形
// SurfaceView 初始化完毕后调用
@Override
public void surfaceCreated(final SurfaceHolder surfaceHolder) {
new Thread(){
@Override
public void run() {
super.run();
while(i<100){
Canvas canvas = surfaceHolder.lockCanvas();
canvas.drawCircle(100+10*i,100+10*i,100,paint);
surfaceHolder.unlockCanvasAndPost(canvas);
SystemClock.sleep(1000);
i++;
}
}
}.start();
}
// 当SurfaceView发生改变后调用
@Override
public void surfaceChanged(SurfaceHolder surfaceHolder, int i, int i1, int i2) {}
// 当SurfaceView被销毁时调用
@Override
public void surfaceDestroyed(SurfaceHolder surfaceHolder) {
i = 101;
}
案例二:
在SurfaceView上绘制一个MediaPlayer,并且当点击SurfaceView时,根据播放状态,对MediaPlayer进行控制
public class MainActivity extends AppCompatActivity {
private SurfaceView svPlay = null;
private String url = "http://183.6.194.19/xdispatch/qiubai-video.qiushibaike.com/IBDKU9U37LCE9XIN.mp4";
private MediaPlayer mediaPlayer = null;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
initView();
mediaPlayer = new MediaPlayer();
// 给MediaPlayer设置监听,当准备好时就开始播放
mediaPlayer.setOnPreparedListener(new MediaPlayer.OnPreparedListener() {
// 当MediaPlayer调用prepare方法是触发该监听器
@Override
public void onPrepared(MediaPlayer mediaPlayer) {
mediaPlayer.start();
}
});
// 获得SurfaceHolder对象
final SurfaceHolder holder = svPlay.getHolder();
holder.addCallback(new SurfaceHolder.Callback() {
@Override
public void surfaceCreated(SurfaceHolder surfaceHolder) {
// 复位(reset)MediaPlayer至未初始化状态。调用此方法后,你需要再次设置数据源,并通过prepare()来初始化
mediaPlayer.reset();
// 设置用于显示媒体视频的SurfaceHolder,不设置的话mediaPlayer不知道在哪里显示即黑屏
mediaPlayer.setDisplay(holder);
try {
mediaPlayer.setDataSource(MainActivity.this, Uri.parse(url));
// 播放网络资源时需要检查网络的状态,播放SD卡的文件时,需要检测文件是否有效
// prepare()方法,预期准备,因为setDataSource()方法之后,MediaPlayer并未真正的
// 去装载那些媒体文件,需要调用prepare()这个方法去准备音频
/**
* 设置完数据源和显示的Surface后,你需要调用prepare()或prepareAsync()
* prepare()同步执行
* prepareAsync()异步执行
*/
// 异步准备数据,准备工作在子线程中完成,播放网络资源时,一般调用此方法
mediaPlayer.prepareAsync();
} catch (IOException e) {
e.printStackTrace();
}
}
@Override
public void surfaceChanged(SurfaceHolder surfaceHolder, int i, int i1, int i2) {
}
@Override
public void surfaceDestroyed(SurfaceHolder surfaceHolder) {
if (mediaPlayer != null) {
mediaPlayer.stop();
// 释放相关该MediaPlayer对象的相关的资源
mediaPlayer.release();
}
}
});
// 给SurfaceView控件添加触摸事件
svPlay.setOnTouchListener(new View.OnTouchListener() {
@Override
public boolean onTouch(View view, MotionEvent motionEvent) {
// 当用户触摸屏幕时将创建一个MotionEvent对象。MotionEvent包含关于发生触摸的位置和时间等细节信息,motionEvent.getAction()方法以得到该Motionevent具体是哪个操作
switch (motionEvent.getAction()){
// 当用户在SurfaceView上有触摸事件,当正在播放,点击暂停,当未在播放,点击播放
case MotionEvent.ACTION_DOWN:
if(mediaPlayer != null){
if(mediaPlayer.isPlaying()){
mediaPlayer.pause();
}else {
mediaPlayer.start();
}
}
break;
}
return false;
}
});
}
private void initView() {
svPlay = (SurfaceView) findViewById(R.id.svPlay);
}
}