android之VideoView和视频播放View的扩展 – Devin Zhang – 博客园

1.概念及扩展

  VideoView 是android 系统提供的一个媒体播放显示和控制的控件。其结构层次如下:

  原型:VideoView extends SurfaceView implements MediaController.MediaPlayerControl

  类结构:

      java.lang.Object
        ↳ android.view.View
          ↳ android.view.SurfaceView
            ↳ android.widget.VideoView

  通过VideoView 的原型可知:如果构建更为复杂和有特色个性的视频View,需要继承SurfaceView 和实现MediaPlayerControl接口其中SurfaceView 为显示提供支持,MediaPlayerControl则为媒体控制提供了支持。

2.案例

1)VideoView案例

(我们没有管理MediaPalyer的各种状态,这些状态都让VideoView给封装了,并且,当VideoView创建的时候,MediaPalyer对象将会创建,当VideoView对象销毁的时候,MediaPlayer对象将会释放。)

布局文件

复制代码

<span style="color: #0000ff;">&lt;?</span><span style="color: #ff00ff;">xml version="1.0" encoding="utf-8"</span><span style="color: #0000ff;">?&gt;</span><br><span style="color: #0000ff;">&lt;</span><span style="color: #800000;">LinearLayout </span><span style="color: #ff0000;">xmlns:android</span><span style="color: #0000ff;">="http://schemas.android.com/apk/res/android"</span><span style="color: #ff0000;"> android:orientation</span><span style="color: #0000ff;">="vertical" </span><span style="color: #ff0000;">android:layout_width</span><span style="color: #0000ff;">="fill_parent"</span><span style="color: #ff0000;"><br>    android:layout_height</span><span style="color: #0000ff;">="fill_parent"</span><span style="color: #0000ff;">&gt;</span><br><span style="color: #0000ff;">&lt;</span><span style="color: #800000;">VideoView </span><span style="color: #ff0000;">android:id</span><span style="color: #0000ff;">="@+id/video_view"</span><span style="color: #ff0000;"> android:layout_width</span><span style="color: #0000ff;">="match_parent"</span><span style="color: #ff0000;"> android:layout_height</span><span style="color: #0000ff;">="match_parent"</span><span style="color: #ff0000;"><br>    android:layout_centerInParent</span><span style="color: #0000ff;">="true"</span> <span style="color: #0000ff;">/&gt;</span><br><span style="color: #0000ff;">&lt;/</span><span style="color: #800000;">LinearLayout</span><span style="color: #0000ff;">&gt;</span>

复制代码

主程序:

复制代码

<span style="color: #0000ff;">public</span> <span style="color: #0000ff;">class</span> VideoPlayer <span style="color: #0000ff;">extends</span> Activity <span style="color: #0000ff;">implements</span> MediaPlayer.OnErrorListener, MediaPlayer.OnCompletionListener {<br>    <span style="color: #0000ff;">public</span> <span style="color: #0000ff;">static</span> <span style="color: #0000ff;">final</span> String TAG = "VideoPlayer";<br>    <span style="color: #0000ff;">private</span> VideoView mVideoView;<br>    <span style="color: #0000ff;">private</span> Uri mUri;<br>    <span style="color: #0000ff;">private</span> <span style="color: #0000ff;">int</span> mPositionWhenPaused = -1;<br><br>    <span style="color: #0000ff;">private</span> MediaController mMediaController;<br><br>    @Override<br>    <span style="color: #0000ff;">public</span> <span style="color: #0000ff;">void</span> onCreate(Bundle savedInstanceState) {<br>        <span style="color: #0000ff;">super</span>.onCreate(savedInstanceState);<br><br>        setContentView(R.layout.main);<br><br>        <span style="color: #008000;">//</span><span style="color: #008000;">Set the screen to landscape.</span><span style="color: #008000;"><br></span>        <span style="color: #0000ff;">this</span>.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);<br><br>        mVideoView = (VideoView)findViewById(R.id.video_view);<br><br>        <span style="color: #008000;">//</span><span style="color: #008000;">Video file</span><span style="color: #008000;"><br></span>        mUri = Uri.parse(Environment.getExternalStorageDirectory() + "/1.3gp");<br><br>        <span style="color: #008000;">//</span><span style="color: #008000;">Create media controller,<span>组件可以控制视频的播放,暂停,回复,seek等操作,不需要你实现</span></span><span style="color: #008000;"><br></span>        mMediaController = <span style="color: #0000ff;">new</span> MediaController(<span style="color: #0000ff;">this</span>);<br>        mVideoView.setMediaController(mMediaController);<br>    }<br><br>    <span style="color: #0000ff;">public</span> <span style="color: #0000ff;">void</span> onStart() {<br>        <span style="color: #008000;">//</span><span style="color: #008000;"> Play Video</span><span style="color: #008000;"><br></span>        mVideoView.setVideoURI(mUri);<br>        mVideoView.start();<br><br>        <span style="color: #0000ff;">super</span>.onStart();<br>    }<br><br>    <span style="color: #0000ff;">public</span> <span style="color: #0000ff;">void</span> onPause() {<br>        <span style="color: #008000;">//</span><span style="color: #008000;"> Stop video when the activity is pause.</span><span style="color: #008000;"><br></span>        mPositionWhenPaused = mVideoView.getCurrentPosition();<br>        mVideoView.stopPlayback();<br><br>        <span style="color: #0000ff;">super</span>.onPause();<br>    }<br><br>    <span style="color: #0000ff;">public</span> <span style="color: #0000ff;">void</span> onResume() {<br>        <span style="color: #008000;">//</span><span style="color: #008000;"> Resume video player</span><span style="color: #008000;"><br></span>        <span style="color: #0000ff;">if</span>(mPositionWhenPaused &gt;= 0) {<br>            mVideoView.seekTo(mPositionWhenPaused);<br>            mPositionWhenPaused = -1;<br>        }<br><br>        <span style="color: #0000ff;">super</span>.onResume();<br>    }<br><br>    <span style="color: #0000ff;">public</span> <span style="color: #0000ff;">boolean</span> onError(MediaPlayer player, <span style="color: #0000ff;">int</span> arg1, <span style="color: #0000ff;">int</span> arg2) {<br>        <span style="color: #0000ff;">return</span> <span style="color: #0000ff;">false</span>;<br>    }<br><br>    <span style="color: #0000ff;">public</span> <span style="color: #0000ff;">void</span> onCompletion(MediaPlayer mp) {<br>        <span style="color: #0000ff;">this</span>.finish();<br>    }<br>}

复制代码

2)自定义VideoView

和VideoView实现类似,继承了SurfaceView并且实现了MediaPlayerControl。

复制代码

<span style="color: #0000ff;">public</span> <span style="color: #0000ff;">class</span> CustomerVideoView <span style="color: #0000ff;">extends</span> SurfaceView <span style="color: #0000ff;">implements</span> <br>        MediaPlayerControl { <br>    <span style="color: #0000ff;">private</span> <span style="color: #0000ff;">static</span> String TAG = "customer.videoplayer"; <br>    <span style="color: #0000ff;">private</span> <span style="color: #0000ff;">boolean</span> pause; <br>    <span style="color: #0000ff;">private</span> <span style="color: #0000ff;">boolean</span> seekBackward; <br>    <span style="color: #0000ff;">private</span> <span style="color: #0000ff;">boolean</span> seekForward; <br>    <span style="color: #0000ff;">private</span> Uri videoUri; <br>    <span style="color: #0000ff;">private</span> MediaPlayer mediaPlayer; <br>    <span style="color: #0000ff;">private</span> Context context; <br>    <span style="color: #0000ff;">private</span> OnPreparedListener onPreparedListener; <br>    <span style="color: #0000ff;">private</span> <span style="color: #0000ff;">int</span> videoWidth; <br>    <span style="color: #0000ff;">private</span> <span style="color: #0000ff;">int</span> videoHeight; <br>    <span style="color: #0000ff;">private</span> MediaController mediaController; <br>    <span style="color: #0000ff;">protected</span> SurfaceHolder surfaceHolder; <br>    <span style="color: #0000ff;">private</span> Callback surfaceHolderCallback = <span style="color: #0000ff;">new</span> SurfaceHolder.Callback() { <br>        <span style="color: #0000ff;">public</span> <span style="color: #0000ff;">void</span> surfaceChanged(SurfaceHolder holder, <span style="color: #0000ff;">int</span> format, <span style="color: #0000ff;">int</span> w, <br>                <span style="color: #0000ff;">int</span> h) { <br>        } <br>        <span style="color: #0000ff;">public</span> <span style="color: #0000ff;">void</span> surfaceCreated(SurfaceHolder holder) { <br>            surfaceHolder = holder; <br>            <span style="color: #0000ff;">if</span> (mediaPlayer != <span style="color: #0000ff;">null</span>) { <br>                mediaPlayer.setDisplay(surfaceHolder); <br>                resume(); <br>            } <span style="color: #0000ff;">else</span> { <br>                openVideo(); <br>            } <br>        } <br>        <span style="color: #0000ff;">public</span> <span style="color: #0000ff;">void</span> surfaceDestroyed(SurfaceHolder holder) { <br>            surfaceHolder = <span style="color: #0000ff;">null</span>; <br>            <span style="color: #0000ff;">if</span> (mediaController != <span style="color: #0000ff;">null</span>) { <br>                mediaController.hide(); <br>            } <br>            release(<span style="color: #0000ff;">true</span>); <br>        } <br>    }; <br>    <span style="color: #0000ff;">private</span> <span style="color: #0000ff;">void</span> release(<span style="color: #0000ff;">boolean</span> cleartargetstate) { <br>        <span style="color: #0000ff;">if</span> (mediaPlayer != <span style="color: #0000ff;">null</span>) { <br>            mediaPlayer.reset(); <br>            mediaPlayer.release(); <br>            mediaPlayer = <span style="color: #0000ff;">null</span>; <br>        } <br>    } <br>    <span style="color: #0000ff;">public</span> <span style="color: #0000ff;">void</span> resume() { <br>        <span style="color: #0000ff;">if</span> (surfaceHolder == <span style="color: #0000ff;">null</span>) { <br>            <span style="color: #0000ff;">return</span>; <br>        } <br>        <span style="color: #0000ff;">if</span> (mediaPlayer != <span style="color: #0000ff;">null</span>) { <br>            <span style="color: #0000ff;">return</span>; <br>        } <br>        openVideo(); <br>    } <br>    <span style="color: #0000ff;">public</span> CustomerVideoView(Context context, AttributeSet attrs, <span style="color: #0000ff;">int</span> defStyle) { <br>        <span style="color: #0000ff;">super</span>(context, attrs, defStyle); <br>        <span style="color: #0000ff;">this</span>.context = context; <br>        <span style="color: #0000ff;">this</span>.initVideoView(); <br>    } <br>    <span style="color: #0000ff;">public</span> CustomerVideoView(Context context, AttributeSet attrs) { <br>        <span style="color: #0000ff;">super</span>(context, attrs); <br>        <span style="color: #0000ff;">this</span>.context = context; <br>        <span style="color: #0000ff;">this</span>.initVideoView(); <br>    } <br>    <span style="color: #0000ff;">public</span> CustomerVideoView(Context context) { <br>        <span style="color: #0000ff;">super</span>(context); <br>        <span style="color: #0000ff;">this</span>.context = context; <br>        <span style="color: #0000ff;">this</span>.initVideoView(); <br>    } <br>    @Override <br>    <span style="color: #0000ff;">public</span> <span style="color: #0000ff;">boolean</span> canPause() { <br>        <span style="color: #0000ff;">return</span> <span style="color: #0000ff;">this</span>.pause; <br>    } <br>    @Override <br>    <span style="color: #0000ff;">public</span> <span style="color: #0000ff;">boolean</span> canSeekBackward() { <br>        <span style="color: #0000ff;">return</span> <span style="color: #0000ff;">this</span>.seekBackward; <br>    } <br>    @Override <br>    <span style="color: #0000ff;">public</span> <span style="color: #0000ff;">boolean</span> canSeekForward() { <br>        <span style="color: #0000ff;">return</span> <span style="color: #0000ff;">this</span>.seekForward; <br>    } <br>    @Override <br>    <span style="color: #0000ff;">public</span> <span style="color: #0000ff;">int</span> getBufferPercentage() { <br>        <span style="color: #0000ff;">return</span> 0; <br>    } <br>    @Override <br>    <span style="color: #0000ff;">public</span> <span style="color: #0000ff;">int</span> getCurrentPosition() { <br>        <span style="color: #0000ff;">return</span> mediaPlayer!=<span style="color: #0000ff;">null</span>?mediaPlayer.getCurrentPosition():0; <br>    } <br>    @Override <br>    <span style="color: #0000ff;">public</span> <span style="color: #0000ff;">int</span> getDuration() { <br>        <span style="color: #0000ff;">return</span> mediaPlayer!=<span style="color: #0000ff;">null</span>?mediaPlayer.getDuration():0; <br>    } <br>    @Override <br>    <span style="color: #0000ff;">public</span> <span style="color: #0000ff;">boolean</span> isPlaying() { <br>        <span style="color: #0000ff;">return</span> <span style="color: #0000ff;">false</span>; <br>    } <br>    @Override <br>    <span style="color: #0000ff;">public</span> <span style="color: #0000ff;">void</span> pause() { <br>    } <br>    @Override <br>    <span style="color: #0000ff;">public</span> <span style="color: #0000ff;">void</span> seekTo(<span style="color: #0000ff;">int</span> mSec) { <br>    } <br>    @Override <br>    <span style="color: #0000ff;">public</span> <span style="color: #0000ff;">void</span> start() { <br>    } <br>    <span style="color: #0000ff;">public</span> <span style="color: #0000ff;">void</span> setVideoURI(Uri uri) { <br>        <span style="color: #0000ff;">this</span>.videoUri = uri; <br>        openVideo(); <br>        requestLayout(); <br>        invalidate(); <br>    } <br>    <span style="color: #0000ff;">private</span> <span style="color: #0000ff;">void</span> openVideo() { <br>        <span style="color: #0000ff;">this</span>.mediaPlayer = <span style="color: #0000ff;">new</span> MediaPlayer(); <br>        <span style="color: #0000ff;">try</span> { <br>            <span style="color: #0000ff;">this</span>.mediaPlayer.setDataSource(<span style="color: #0000ff;">this</span>.context, <span style="color: #0000ff;">this</span>.videoUri); <br>        } <span style="color: #0000ff;">catch</span> (Exception e) { <br>            Log.e(TAG, e.getMessage()); <br>            <span style="color: #0000ff;">throw</span> <span style="color: #0000ff;">new</span> RuntimeException(e); <br>        } <br>        <span style="color: #0000ff;">this</span>.mediaPlayer.prepareAsync(); <br>        <span style="color: #0000ff;">this</span>.mediaPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC); <br>        <span style="color: #0000ff;">this</span>.mediaPlayer.setOnPreparedListener(onPreparedListener); <br>        attachMediaController(); <br>    } <br>    <span style="color: #0000ff;">private</span> <span style="color: #0000ff;">void</span> attachMediaController() { <br>        <span style="color: #0000ff;">if</span> (mediaPlayer != <span style="color: #0000ff;">null</span> &amp;&amp; mediaController != <span style="color: #0000ff;">null</span>) { <br>            mediaController.setMediaPlayer(<span style="color: #0000ff;">this</span>); <br>            View anchorView = <span style="color: #0000ff;">this</span>.getParent() <span style="color: #0000ff;">instanceof</span> View ? (View) <span style="color: #0000ff;">this</span> <br>                    .getParent() : <span style="color: #0000ff;">this</span>; <br>            mediaController.setAnchorView(anchorView); <br>            mediaController.setEnabled(<span style="color: #0000ff;">true</span>); <br>        } <br>    } <br>    <span style="color: #0000ff;">public</span> <span style="color: #0000ff;">void</span> setMediaController(MediaController controller) { <br>        <span style="color: #0000ff;">if</span> (mediaController != <span style="color: #0000ff;">null</span>) { <br>            mediaController.hide(); <br>        } <br>        mediaController = controller; <br>        attachMediaController(); <br>    } <br>    <span style="color: #0000ff;">public</span> <span style="color: #0000ff;">void</span> setOnPreparedListener(OnPreparedListener onPreparedListener) { <br>        <span style="color: #0000ff;">this</span>.onPreparedListener = onPreparedListener; <br>    } <br>    @Override <br>    <span style="color: #0000ff;">protected</span> <span style="color: #0000ff;">void</span> onMeasure(<span style="color: #0000ff;">int</span> widthMeasureSpec, <span style="color: #0000ff;">int</span> heightMeasureSpec) { <br>        <span style="color: #0000ff;">int</span> width = getDefaultSize(videoWidth, widthMeasureSpec); <br>        <span style="color: #0000ff;">int</span> height = getDefaultSize(videoHeight, heightMeasureSpec); <br>        <span style="color: #0000ff;">if</span> (videoWidth &gt; 0 &amp;&amp; videoHeight &gt; 0) { <br>            <span style="color: #0000ff;">if</span> (videoWidth * height &gt; width * videoHeight) { <br>                height = width * videoHeight / videoWidth; <br>            } <span style="color: #0000ff;">else</span> <span style="color: #0000ff;">if</span> (videoWidth * height &lt; width * videoHeight) { <br>                width = height * videoWidth / videoHeight; <br>            } <br>        } <br>        Log.i(TAG, "setting size: " + width + ‘x’ + height); <br>        setMeasuredDimension(width, height); <br>    } <br>    <span style="color: #0000ff;">private</span> <span style="color: #0000ff;">void</span> initVideoView() { <br>        videoWidth = 0; <br>        videoHeight = 0; <br>        getHolder().addCallback(surfaceHolderCallback); <br>        getHolder().setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS); <br>        setFocusable(<span style="color: #0000ff;">true</span>); <br>        setFocusableInTouchMode(<span style="color: #0000ff;">true</span>); <br>        requestFocus(); <br>    } <br>}

复制代码

  一般情况下,android界面的绘制和更新,要交给主ui线程来操作,通过Handler机制。但是播放视频,需要比较优先和实时的改变和绘制界面。android提供了使用单独线程绘制UI的机制,就是SurfaceView。使用SurfaceView,需要实现SurfaceHolder.Callback接口:

  • surfaceCreated,在Surface(SurfaceView内部包含一个Surface实例)创建后,会立即调用该方法,可在该方法中做绘制界面相关的初始化工作;
  • surfaceChanged,当Surface的状态发生变化,比如大小,会调用该方法,在surfaceCreated方法调用过至少会调用一次该方法;
  • surfaceDestroyed,当销毁Surface的时候调用。

  开发者不能直接操作Surface实例,要通过SurfaceHandler,在SurfaceView中可以通过getHandler方法获取到SurfaceHandler实例。
SurfaceHander有一些类型,用来标识Surface实例界面数据来源,可以通过setType来操作:

  • SURFACE_TYPE_NORMAL:RAM缓存的原生数据
  • SURFACE_TYPE_HARDWARE:通过DMA,direct memory access,就是直接写屏技术获取到的数据,或者其他硬件加速的数据
  • SURFACE_TYPE_GPU:通过GPU加速的数据
  • SURFACE_TYPE_PUSH_BUFFERS:标识数据来源于其他对象,比如照相机,比如视频播放服务器(android内部有视频播放的服务器,所有播放视频相当于客户端)

  CustomerVideoView的构造方法,使用超类的构造方法。都会执行initVideoView()方法用来初始化界面和参数。另外一个主要的内容是openVideo()方法:

  • mediaPlayer.prepareAsync(),用来异步准备播放,另外还有个prepare()方法,是同步的,也就是全部下载完毕才能播放,显然,在播放网上视频的时候需要用前者;
  • 通过attachMediaController()方法,把控制条附加到播放视频的SurfaceView上,这里实现的不完全,因此还不能使用,仅仅是把MediaPlayerControl实例通过setMediaPlayer方法设置一下,供OnPreparedListener用来得到加载成功的回调,另外供外面代码调用得到视频的时长和当前时长。

来源URL:http://www.cnblogs.com/devinzhang/archive/2012/02/04/2338125.html