Welcome

首页 / 移动开发 / Android / Android VideoView类实例讲解

        本节使用系统的示例类VideoView继续SurfaceView类相关内容的讲解,以让大家能更深入理解Android系统中图形绘制基础类的实现原理。也许你会发现无法改变VideoView类的控制方面,我们可以通过重构VideoView类来实现更加个性化的播放器。
         下面是VideoView类的相关代码。
Java 代码
 public class VideoView extends SurfaceView implements MediaPlayerControl {private String TAG = "VideoView";// settable by the clientprivate Uri mUri;private int mDuration; // all possible internal statesprivate static final int STATE_ERROR= -1;private static final int STATE_IDLE= 0;private static final int STATE_PREPARING = 1;private static final int STATE_PREPARED = 2;private static final int STATE_PLAYING = 3;private static final int STATE_PAUSED= 4;private static final int STATE_PLAYBACK_COMPLETED = 5; // mCurrentState is a VideoView object"s current state.// mTargetState is the state that a method caller intends to reach.// For instance, regardless the VideoView object"s current state,// calling pause() intends to bring the object to a target state// of STATE_PAUSED.private int mCurrentState = STATE_IDLE;private int mTargetState = STATE_IDLE; // All the stuff we need for playing and showing a videoprivate SurfaceHolder mSurfaceHolder = null;private MediaPlayer mMediaPlayer = null;private int mVideoWidth;private int mVideoHeight;private int mSurfaceWidth;private int mSurfaceHeight;private MediaController mMediaController;private OnCompletionListener mOnCompletionListener;private MediaPlayer.OnPreparedListener mOnPreparedListener;private int mCurrentBufferPercentage;private OnErrorListener mOnErrorListener;private int mSeekWhenPrepared; // recording the seek position while preparingprivate booleanmCanPause;private booleanmCanSeekBack;private booleanmCanSeekForward; public VideoView(Context context) { super(context); initVideoView();}public VideoView(Context context, AttributeSet attrs) { this(context, attrs, 0); initVideoView();}public VideoView(Context context, AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); initVideoView();} @Overrideprotected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { //Log.i("@@@@", "onMeasure"); int width = getDefaultSize(mVideoWidth, widthMeasureSpec); int height = getDefaultSize(mVideoHeight, heightMeasureSpec); if (mVideoWidth > 0 && mVideoHeight > 0) {if ( mVideoWidth * height > width * mVideoHeight ) { //Log.i("@@@", "image too tall, correcting"); height = width * mVideoHeight / mVideoWidth;} else if ( mVideoWidth * height < width * mVideoHeight ) { //Log.i("@@@", "image too wide, correcting"); width = height * mVideoWidth / mVideoHeight;} else { //Log.i("@@@", "aspect ratio is correct: " + //width+"/"+height+"="+ //mVideoWidth+"/"+mVideoHeight);} } //Log.i("@@@@@@@@@@", "setting size: " + width + "x" + height); setMeasuredDimension(width, height);}public int resolveAdjustedSize(int desiredSize, int measureSpec) { int result = desiredSize; int specMode = MeasureSpec.getMode(measureSpec); int specSize = MeasureSpec.getSize(measureSpec);switch (specMode) {case MeasureSpec.UNSPECIFIED: result = desiredSize; break; case MeasureSpec.AT_MOST: /* Parent says we can be as big as we want, up to specSize.* Don"t be larger than specSize, and don"t be larger than* the max size imposed on ourselves.*/ result = Math.min(desiredSize, specSize); break; case MeasureSpec.EXACTLY: // No choice. Do what we are told. result = specSize; break; } return result; }private void initVideoView() { mVideoWidth = 0; mVideoHeight = 0; getHolder().addCallback(mSHCallback); getHolder().setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS); setFocusable(true); setFocusableInTouchMode(true); requestFocus(); mCurrentState = STATE_IDLE; mTargetState = STATE_IDLE;} public void setVideoPath(String path) { setVideoURI(Uri.parse(path));} public void setVideoURI(Uri uri) { mUri = uri; mSeekWhenPrepared = 0; openVideo(); requestLayout(); invalidate();}public void stopPlayback() { if (mMediaPlayer != null) {mMediaPlayer.stop();mMediaPlayer.release();mMediaPlayer = null;mCurrentState = STATE_IDLE;mTargetState = STATE_IDLE; }} private void openVideo() { if (mUri == null || mSurfaceHolder == null) {// not ready for playback just yet, will try again laterreturn; } // Tell the music playback service to pause // TODO: these constants need to be published somewhere in the framework. Intent i = new Intent("com.android.music.musicservicecommand"); i.putExtra("command", "pause"); mContext.sendBroadcast(i);// we shouldn"t clear the target state, because somebody might have // called start() previously release(false); try {mMediaPlayer = new MediaPlayer();mMediaPlayer.setOnPreparedListener(mPreparedListener);mMediaPlayer.setOnVideoSizeChangedListener(mSizeChangedListener);mDuration = -1;mMediaPlayer.setOnCompletionListener(mCompletionListener);mMediaPlayer.setOnErrorListener(mErrorListener);mMediaPlayer.setOnBufferingUpdateListener(mBufferingUpdateListener);mCurrentBufferPercentage = 0;mMediaPlayer.setDataSource(mContext, mUri);mMediaPlayer.setDisplay(mSurfaceHolder);mMediaPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC);mMediaPlayer.setScreenOnWhilePlaying(true);mMediaPlayer.prepareAsync();// we don"t set the target state here either, but preserve the// target state that was there before.mCurrentState = STATE_PREPARING;attachMediaController(); } catch (IOException ex) {Log.w(TAG, "Unable to open content: " + mUri, ex);mCurrentState = STATE_ERROR;mTargetState = STATE_ERROR;mErrorListener.onError(mMediaPlayer, MediaPlayer.MEDIA_ERROR_UNKNOWN, 0);return; } catch (IllegalArgumentException ex) {Log.w(TAG, "Unable to open content: " + mUri, ex);mCurrentState = STATE_ERROR;mTargetState = STATE_ERROR;mErrorListener.onError(mMediaPlayer, MediaPlayer.MEDIA_ERROR_UNKNOWN, 0);return; }}public void setMediaController(MediaController controller) { if (mMediaController != null) {mMediaController.hide(); } mMediaController = controller; attachMediaController();} private void attachMediaController() { if (mMediaPlayer != null && mMediaController != null) {mMediaController.setMediaPlayer(this);View anchorView = this.getParent() instanceof View ?(View)this.getParent() : this;mMediaController.setAnchorView(anchorView);mMediaController.setEnabled(isInPlaybackState()); }}MediaPlayer.OnVideoSizeChangedListener mSizeChangedListener = new MediaPlayer.OnVideoSizeChangedListener() {public void onVideoSizeChanged(MediaPlayer mp, int width, int height) { mVideoWidth = mp.getVideoWidth(); mVideoHeight = mp.getVideoHeight(); if (mVideoWidth != 0 && mVideoHeight != 0) {getHolder().setFixedSize(mVideoWidth, mVideoHeight); }}};MediaPlayer.OnPreparedListener mPreparedListener = new MediaPlayer.OnPreparedListener() { public void onPrepared(MediaPlayer mp) {mCurrentState = STATE_PREPARED; // Get the capabilities of the player for this streamMetadata data = mp.getMetadata(MediaPlayer.METADATA_ALL, MediaPlayer.BYPASS_METADATA_FILTER); if (data != null) { mCanPause = !data.has(Metadata.PAUSE_AVAILABLE) || data.getBoolean(Metadata.PAUSE_AVAILABLE); mCanSeekBack = !data.has(Metadata.SEEK_BACKWARD_AVAILABLE) || data.getBoolean(Metadata.SEEK_BACKWARD_AVAILABLE); mCanSeekForward = !data.has(Metadata.SEEK_FORWARD_AVAILABLE) || data.getBoolean(Metadata.SEEK_FORWARD_AVAILABLE);} else { mCanPause = mCanSeekForward = mCanSeekForward = true;} if (mOnPreparedListener != null) { mOnPreparedListener.onPrepared(mMediaPlayer);}if (mMediaController != null) { mMediaController.setEnabled(true);}mVideoWidth = mp.getVideoWidth();mVideoHeight = mp.getVideoHeight(); int seekToPosition = mSeekWhenPrepared; // mSeekWhenPrepared may be changed after seekTo() callif (seekToPosition != 0) { seekTo(seekToPosition);}if (mVideoWidth != 0 && mVideoHeight != 0) { //Log.i("@@@@", "video size: " + mVideoWidth +"/"+ mVideoHeight); getHolder().setFixedSize(mVideoWidth, mVideoHeight); if (mSurfaceWidth == mVideoWidth && mSurfaceHeight == mVideoHeight) {// We didn"t actually change the size (it was already at the size// we need), so we won"t get a "surface changed" callback, so// start the video here instead of in the callback.if (mTargetState == STATE_PLAYING) { start(); if (mMediaController != null) {mMediaController.show(); }} else if (!isPlaying() && (seekToPosition != 0 || getCurrentPosition() > 0)) { if (mMediaController != null) {// Show the media controls when we"re paused into a video and make "em stick.mMediaController.show(0); }} }} else { // We don"t know the video size yet, but should start anyway. // The video size might be reported to us later. if (mTargetState == STATE_PLAYING) {start(); }} }}; private MediaPlayer.OnCompletionListener mCompletionListener = new MediaPlayer.OnCompletionListener() { public void onCompletion(MediaPlayer mp) {mCurrentState = STATE_PLAYBACK_COMPLETED;mTargetState = STATE_PLAYBACK_COMPLETED;if (mMediaController != null) { mMediaController.hide();}if (mOnCompletionListener != null) { mOnCompletionListener.onCompletion(mMediaPlayer);} }}; private MediaPlayer.OnErrorListener mErrorListener = new MediaPlayer.OnErrorListener() { public boolean onError(MediaPlayer mp, int framework_err, int impl_err) {Log.d(TAG, "Error: " + framework_err + "," + impl_err);mCurrentState = STATE_ERROR;mTargetState = STATE_ERROR;if (mMediaController != null) { mMediaController.hide();} /* If an error handler has been supplied, use it and finish. */if (mOnErrorListener != null) { if (mOnErrorListener.onError(mMediaPlayer, framework_err, impl_err)) {return true; }} /* Otherwise, pop up an error dialog so the user knows that * something bad has happened. Only try and pop up the dialog * if we"re attached to a window. When we"re going away and no * longer have a window, don"t bother showing the user an error. */if (getWindowToken() != null) { Resources r = mContext.getResources(); int messageId;if (framework_err == MediaPlayer.MEDIA_ERROR_NOT_VALID_FOR_PROGRESSIVE_PLAYBACK) {messageId = com.android.internal.R.string.VideoView_error_text_invalid_progressive_playback; } else {messageId = com.android.internal.R.string.VideoView_error_text_unknown; }new AlertDialog.Builder(mContext) .setTitle(com.android.internal.R.string.VideoView_error_title) .setMessage(messageId) .setPositiveButton(com.android.internal.R.string.VideoView_error_button, new DialogInterface.OnClickListener() {public void onClick(DialogInterface dialog, int whichButton) { /* If we get here, there is no onError listener, so* at least inform them that the video is over.*/ if (mOnCompletionListener != null) {mOnCompletionListener.onCompletion(mMediaPlayer); }} }) .setCancelable(false) .show();}return true; }}; private MediaPlayer.OnBufferingUpdateListener mBufferingUpdateListener = new MediaPlayer.OnBufferingUpdateListener() { public void onBufferingUpdate(MediaPlayer mp, int percent) {mCurrentBufferPercentage = percent; }}; /** * Register a callback to be invoked when the media file * is loaded and ready to go. * * @param l The callback that will be run */public void setOnPreparedListener(MediaPlayer.OnPreparedListener l){ mOnPreparedListener = l;} /** * Register a callback to be invoked when the end of a media file * has been reached during playback. * * @param l The callback that will be run */public void setOnCompletionListener(OnCompletionListener l){ mOnCompletionListener = l;} /** * Register a callback to be invoked when an error occurs * during playback or setup. If no listener is specified, * or if the listener returned false, VideoView will inform * the user of any errors. * * @param l The callback that will be run */public void setOnErrorListener(OnErrorListener l){ mOnErrorListener = l;} SurfaceHolder.Callback mSHCallback = new SurfaceHolder.Callback(){ public void surfaceChanged(SurfaceHolder holder, int format,int w, int h) {mSurfaceWidth = w;mSurfaceHeight = h;boolean isValidState = (mTargetState == STATE_PLAYING);boolean hasValidSize = (mVideoWidth == w && mVideoHeight == h);if (mMediaPlayer != null && isValidState && hasValidSize) { if (mSeekWhenPrepared != 0) {seekTo(mSeekWhenPrepared); } start(); if (mMediaController != null) {mMediaController.show(); }} }public void surfaceCreated(SurfaceHolder holder) {mSurfaceHolder = holder;openVideo(); }public void surfaceDestroyed(SurfaceHolder holder) {// after we return from this we can"t use the surface any moremSurfaceHolder = null;if (mMediaController != null) mMediaController.hide();release(true); }};private void release(boolean cleartargetstate) { if (mMediaPlayer != null) {mMediaPlayer.reset();mMediaPlayer.release();mMediaPlayer = null;mCurrentState = STATE_IDLE;if (cleartargetstate) { mTargetState = STATE_IDLE;} }} @Overridepublic boolean onTouchEvent(MotionEvent ev) { if (isInPlaybackState() && mMediaController != null) {toggleMediaControlsVisiblity(); } return false;}@Overridepublic boolean onTrackballEvent(MotionEvent ev) { if (isInPlaybackState() && mMediaController != null) {toggleMediaControlsVisiblity(); } return false;}@Overridepublic boolean onKeyDown(int keyCode, KeyEvent event){ boolean isKeyCodeSupported = keyCode != KeyEvent.KEYCODE_BACK && keyCode != KeyEvent.KEYCODE_VOLUME_UP && keyCode != KeyEvent.KEYCODE_VOLUME_DOWN && keyCode != KeyEvent.KEYCODE_MENU && keyCode != KeyEvent.KEYCODE_CALL && keyCode != KeyEvent.KEYCODE_ENDCALL; if (isInPlaybackState() && isKeyCodeSupported && mMediaController != null) {if (keyCode == KeyEvent.KEYCODE_HEADSETHOOK ||keyCode == KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE) { if (mMediaPlayer.isPlaying()) {pause();mMediaController.show(); } else {start();mMediaController.hide(); } return true;} else if (keyCode == KeyEvent.KEYCODE_MEDIA_STOP&& mMediaPlayer.isPlaying()) { pause(); mMediaController.show();} else { toggleMediaControlsVisiblity();} }return super.onKeyDown(keyCode, event);} private void toggleMediaControlsVisiblity() { if (mMediaController.isShowing()) {mMediaController.hide(); } else {mMediaController.show(); }}public void start() { if (isInPlaybackState()) {mMediaPlayer.start();mCurrentState = STATE_PLAYING; } mTargetState = STATE_PLAYING;}public void pause() { if (isInPlaybackState()) {if (mMediaPlayer.isPlaying()) { mMediaPlayer.pause(); mCurrentState = STATE_PAUSED;} } mTargetState = STATE_PAUSED;}// cache duration as mDuration for faster accesspublic int getDuration() { if (isInPlaybackState()) {if (mDuration > 0) { return mDuration;}mDuration = mMediaPlayer.getDuration();return mDuration; } mDuration = -1; return mDuration;}public int getCurrentPosition() { if (isInPlaybackState()) {return mMediaPlayer.getCurrentPosition(); } return 0;}public void seekTo(int msec) { if (isInPlaybackState()) {mMediaPlayer.seekTo(msec);mSeekWhenPrepared = 0; } else {mSeekWhenPrepared = msec; }} public boolean isPlaying() { return isInPlaybackState() && mMediaPlayer.isPlaying();}public int getBufferPercentage() { if (mMediaPlayer != null) {return mCurrentBufferPercentage; } return 0;} private boolean isInPlaybackState() { return (mMediaPlayer != null && mCurrentState != STATE_ERROR && mCurrentState != STATE_IDLE && mCurrentState != STATE_PREPARING);} public boolean canPause() { return mCanPause;} public boolean canSeekBackward() { return mCanSeekBack;} public boolean canSeekForward() { return mCanSeekForward;} } 
       以上就是对Android VideoView 类的详细介绍,后续继续补充相关知识,谢谢大家对本站的支持!