Android VideoView类实例讲解

 更新时间:2016年08月22日 16:52:14   投稿:lqh  
本文主要介绍Android VideoView类,这里对VideoView类详细说明了使用方法,以及示例代码,有兴趣的朋友可以参考下,希望能帮助Android 开发的朋友

        本节使用系统的示例类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 类的详细介绍,后续继续补充相关知识,谢谢大家对本站的支持!

相关文章

  • Android自定义弹窗提醒控件使用详解

    Android自定义弹窗提醒控件使用详解

    这篇文章主要为大家详细介绍了Android自定义弹窗提醒控件的使用方法,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2018-07-07
  • Flutter路由管理插件fluro使用简介

    Flutter路由管理插件fluro使用简介

    这篇文章主要介绍了Flutter路由管理插件fluro使用简介,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧
    2021-11-11
  • Android开发中MJRefresh自定义刷新动画效果

    Android开发中MJRefresh自定义刷新动画效果

    本文给大家介绍了MJRefresh自定义刷新动画效果,包括常见用法等相关知识,非常不错,具有参考借鉴价值,感兴趣的朋友一起看看吧
    2016-11-11
  • Android 自定义view实现进度条加载效果实例代码

    Android 自定义view实现进度条加载效果实例代码

    这篇文章主要介绍了Android 自定义view实现进度条加载效果实例代码,需要的朋友可以参考下
    2017-08-08
  • Kotlin中双冒号::使用方法

    Kotlin中双冒号::使用方法

    这篇文章主要给大家介绍了关于Kotlin中双冒号::使用的相关资料,文中通过示例代码介绍的非常详细,对大家学习或者使用Kotlin具有一定的参考学习价值,需要的朋友们下面来一起学习学习吧
    2019-09-09
  • Android加载长图的多种方案分享

    Android加载长图的多种方案分享

    这篇文章主要介绍了Android加载长图的多种方案分享,帮助大家更好的理解和学习使用Android开发,感兴趣的朋友可以了解下
    2021-04-04
  • Mac Android Studio安装图文教程

    Mac Android Studio安装图文教程

    本文主要介绍了Mac Android Studio安装教程,文中通过图文代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2021-08-08
  • Android Service启动过程完整分析

    Android Service启动过程完整分析

    这篇文章主要为大家详细分析了Android Service启动完整过程,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2016-10-10
  • Android EditText实现关键词批量搜索示例

    Android EditText实现关键词批量搜索示例

    本篇文章主要介绍了Android EditText实现关键词批量搜索示例,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧
    2017-02-02
  • Android中使用RecyclerView实现下拉刷新和上拉加载

    Android中使用RecyclerView实现下拉刷新和上拉加载

    RecyclerView 是Android L版本中新添加的一个用来取代ListView的SDK,它的灵活性与可替代性比listview更好。这篇文章主要介绍了Android中使用RecyclerView实现下拉刷新和上拉加载的相关资料,需要的朋友可以参考下
    2016-03-03

最新评论