Android VideoView类实例讲解

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

(0)

相关推荐

  • android视频播放简单实现示例(VideoView&MediaPlayer)

    如果你看过我的<android音乐播放简单实现(MediaPlayer)>,那么本篇将会毫无压力. 首先是主界面的三个按钮和一个播放控件 <?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://sche

  • Android自定义播放器控件VideoView

    介绍 最近要使用播放器做一个简单的视频播放功能,开始学习VideoView,在横竖屏切换的时候碰到了点麻烦,不过在查阅资料后总算是解决了.在写VideoView播放视频时候定义控制的代码全写在Actvity里了,写完一看我靠代码好乱,于是就写了个自定义的播放器控件,支持指定大小,可以横竖屏切换,手动左右滑动快进快退.好了,下面开始. 效果图有点卡,我也不知道为啥..... VideoView介绍 这个是我们实现视频播放最主要的控件,详细的介绍大家百度就去看,这里介绍几个常用的方法. 用于播放视频

  • 详解Android App中使用VideoView来实现视频播放的方法

    通过VideoView播放视频的步骤: 1.在界面布局文件中定义VideoView组件,或在程序中创建VideoView组件 2.调用VideoView的如下两个方法来加载指定的视频 (1)setVidePath(String path):加载path文件代表的视频 (2)setVideoURI(Uri uri):加载uri所对应的视频 3.调用VideoView的start().stop().psuse()方法来控制视频的播放 VideoView通过与MediaController类结合使用,

  • Android编程实现VideoView循环播放功能的方法

    本文实例讲述了Android编程实现VideoView循环播放功能的方法.分享给大家供大家参考,具体如下: package com.hangcheng; import com.example.bdcustomer_demo.R; import com.hangcheng.view.MyVideoView; import android.app.Activity; import android.media.MediaPlayer; import android.os.Bundle; import

  • android使用videoview播放视频

    复制代码 代码如下: public class Activity01 extends Activity{ /** Called when the activity is first created. */ @Override public void onCreate(Bundle savedInstanceState) {  super.onCreate(savedInstanceState); setContentView(R.layout.main); final VideoView vid

  • Android使用VideoView播放本地视频和网络视频的方法

    1.效果展示 2.布局文件 <?xml version="1.0" encoding="utf-8"?> <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:layout_width="matc

  • Android多媒体之VideoView视频播放器

    本文实例为大家分享了视频播放器的两种方式,供大家参考,具体内容如下 1).SurfaceView 在布局文件中 <?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/too

  • android之视频播放系统VideoView和自定义VideoView控件的应用

    Android播放视频,包含系统自带VideoView控件,和自定义VideoView控件,可全屏播放,案例包含了本地视频和网络视频. 1:自定义VideoView控件 2:布局代码 3:Activity代码: 4:网络权限 5:效果图 小结:其中的Uri mUri = Uri.parse("android.resource://" + getPackageName() +"/"+ R.raw.qiche);//本地视频 是加载的本地视频,可以下载一个视频,在res

  • Android videoview抢占焦点的处理方法

    问题描述: android 机顶盒应用: 应用程序主界面(MainActivity)只有两个控件,一个videoview和一个button. 视频框设置无焦点,按键有焦点. 首次进入应用时焦点正常,聚焦在button上:但是当点击button跳转到下一个activity并返回时,焦点却聚焦到了videoview上,并且代码里强制设置焦点: Button.requestFocus(); 无效,现象是视频加载时,焦点是在button处,但是当视频加载完成开始播放时,整个activity失去焦点,几秒

  • android多媒体类VideoView使用方法详解

    一.概述 VideoView类将视频的显示和控制集于一身,我们可以借助它完成一个简易的视频播放器.VideoView和MediaPlayer也比较相似. 二.VideoView的使用方法 它主要有以下几种常用方法 步骤: 1.指定视频文件的路径, 2.接下来调用start()方法就可以开始播放视频,pause()方法就会暂停播放,resume()方法就会重新播放 注:获取视频文件也需要运行时权限,所有相关逻辑也需要写.       最后不要忘记在AndroidManifest.xml文件中声明用

随机推荐