Days36VideoView&SurfaceView&Volly

本文详细介绍了Android中VideoView和SurfaceView的使用方法及原理。包括如何设置视频资源、控制播放状态,以及SurfaceView的绘图机制和线程处理方式。通过实例展示了如何实现视频播放和触摸控制。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

(一)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);
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值