首页 > 系统 > Android > 正文

Android VideoView类实例讲解

2019-12-12 05:34:24
字体:
来源:转载
供稿:网友

        本节使用系统的示例类VideoView继续SurfaceView类相关内容的讲解,以让大家能更深入理解Android系统中图形绘制基础类的实现原理。也许你会发现无法改变VideoView类的控制方面,我们可以通过重构VideoView类来实现更加个性化的播放器。

         下面是VideoView类的相关代码。

Java 代码

 public class VideoView extends SurfaceView implements MediaPlayerControl {  private String TAG = "VideoView";  // settable by the client  private Uri   mUri;  private int   mDuration;   // all possible internal states  private 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 video  private 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 preparing  private boolean  mCanPause;  private boolean  mCanSeekBack;  private boolean  mCanSeekForward;   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();  }   @Override  protected 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 later    return;   }   // 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 stream    Metadata 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() call    if (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 more    mSurfaceHolder = 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;    }   }  }   @Override  public boolean onTouchEvent(MotionEvent ev) {   if (isInPlaybackState() && mMediaController != null) {    toggleMediaControlsVisiblity();   }   return false;  }    @Override  public boolean onTrackballEvent(MotionEvent ev) {   if (isInPlaybackState() && mMediaController != null) {    toggleMediaControlsVisiblity();   }   return false;  }    @Override  public 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 access  public 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 类的详细介绍,后续继续补充相关知识,谢谢大家对本站的支持!

发表评论 共有条评论
用户名: 密码:
验证码: 匿名发表