赞
踩
VideoView
继承SurfaceView
,并且实现了MediaPlayerControl
接口,相当于MediaPlayer+SurfaceView
的组合。关于MediaPlayer
可参考这里。
在布局文件里添加VideoView
<VideoView
android:id="@+id/video_view"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
在Activity
里面播放视频
var mVideoView = findViewById(R.id.video_view)
mVideoView.setVideoURI(Uri.parse(uri))
// 设置控制器
mVideoView.setMediaController(MediaController(this))
mVideoView.start()
主要方法
// 设置视频源 void setVideoPath(String path) void setVideoURI(Uri uri) void setVideoURI(Uri uri, Map<String, String> headers) // 恢复,从头播放 void resume() // 播放 void start() // 暂停 void pause() // 释放资源 void suspend() // 停止播放 void stopPlayback() // 视频时长 int getDuration() // 当前位置 int getCurrentPosition() // 跳转到指定时间 void seekTo(int msec) // 是否正在播放 boolean isPlaying() // 设置控制器 void setMediaController(MediaController controller)
VideoView
里面有mCurrentState
和mTargetState
两个状态,当前状态和目标状态,初始值都是STATE_IDLE
,播放的时候都与这两个状态有关。
VideoView
里面的setVideoXXX
和resume
方法都直接调用openVideo
,在openVideo
方法里面,调用MediaPlayer
播放视频。
mPreparedListener
在MediaPlayer
准备好播放资源后,如果mSeekWhenPrepared
被设置,跳转到指定时间。而如果mTargetState
被设置为STATE_PLAYING
,直接播放。
public void setVideoPath(String path) { setVideoURI(Uri.parse(path)); } public void setVideoURI(Uri uri) { setVideoURI(uri, null); } public void setVideoURI(Uri uri, Map<String, String> headers) { mUri = uri; mHeaders = headers; mSeekWhenPrepared = 0; openVideo(); requestLayout(); invalidate(); } public void resume() { openVideo(); } private void openVideo() { if (mUri == null || mSurfaceHolder == null) { // not ready for playback just yet, will try again later return; } // 清空mCurrentState,但保留mTargetState release(false); mMediaPlayer = new MediaPlayer(); mMediaPlayer.setOnPreparedListener(mPreparedListener); mMediaPlayer.setOnVideoSizeChangedListener(mSizeChangedListener); mMediaPlayer.setOnCompletionListener(mCompletionListener); mMediaPlayer.setDataSource(mContext, mUri, mHeaders); mMediaPlayer.setDisplay(mSurfaceHolder); mMediaPlayer.prepareAsync(); mCurrentState = STATE_PREPARING; } private void release(boolean cleartargetstate) { if (mMediaPlayer != null) { mMediaPlayer.reset(); mMediaPlayer.release(); mMediaPlayer = null; mPendingSubtitleTracks.clear(); mCurrentState = STATE_IDLE; if (cleartargetstate) { mTargetState = STATE_IDLE; } if (mAudioFocusType != AudioManager.AUDIOFOCUS_NONE) { mAudioManager.abandonAudioFocus(null); } } } MediaPlayer.OnPreparedListener mPreparedListener = new MediaPlayer.OnPreparedListener() { public void onPrepared(MediaPlayer mp) { mCurrentState = STATE_PREPARED; if (mOnPreparedListener != null) { mOnPreparedListener.onPrepared(mMediaPlayer); } int seekToPosition = mSeekWhenPrepared; if (seekToPosition != 0) { seekTo(seekToPosition); } if (mVideoWidth != 0 && mVideoHeight != 0) { getHolder().setFixedSize(mVideoWidth, mVideoHeight); if (mSurfaceWidth == mVideoWidth && mSurfaceHeight == mVideoHeight) { if (mTargetState == STATE_PLAYING) { start(); } else if (!isPlaying() && (seekToPosition != 0 || getCurrentPosition() > 0)) { if (mMediaController != null) { mMediaController.show(0); } } } } else { if (mTargetState == STATE_PLAYING) { start(); } } } };
VideoView
里面start
、pause
方法都是直接调用MediaPlayer
的对应方法。suspend
和stopPlayback
都是停止播放并释放资源,但stopPlayback
会修改mTargetState
。
@Override public void start() { if (isInPlaybackState()) { mMediaPlayer.start(); mCurrentState = STATE_PLAYING; } mTargetState = STATE_PLAYING; } @Override public void pause() { if (isInPlaybackState()) { if (mMediaPlayer.isPlaying()) { mMediaPlayer.pause(); mCurrentState = STATE_PAUSED; } } mTargetState = STATE_PAUSED; } public void suspend() { release(false); } public void stopPlayback() { if (mMediaPlayer != null) { mMediaPlayer.stop(); mMediaPlayer.release(); mMediaPlayer = null; mCurrentState = STATE_IDLE; mTargetState = STATE_IDLE; mAudioManager.abandonAudioFocus(null); } }
getDuration
、getCurrentPosition
和seekTo
也都是调用MediaPlayer
的对应方法,只是包装了一层状态的校验,比直接调用更合理。
public int getDuration() { if (isInPlaybackState()) { return mMediaPlayer.getDuration(); } return -1; } @Override public int getCurrentPosition() { if (isInPlaybackState()) { return mMediaPlayer.getCurrentPosition(); } return 0; } @Override public void seekTo(int msec) { if (isInPlaybackState()) { mMediaPlayer.seekTo(msec); mSeekWhenPrepared = 0; } else { mSeekWhenPrepared = msec; } }
MediaController
控制器是VideoView
默认的控制器。
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()); } }
MediaController
创建界面,一般包含3个按钮,从左往右分别是倒退、播放/暂停和快进。底部从左到右是当前时间、进度条和总播放时间。拖动进度条可以跳转到指定时间。
public void setMediaPlayer(MediaPlayerControl player) { mPlayer = player; updatePausePlay(); } public void setAnchorView(View view) { if (mAnchor != null) { mAnchor.removeOnLayoutChangeListener(mLayoutChangeListener); } mAnchor = view; if (mAnchor != null) { mAnchor.addOnLayoutChangeListener(mLayoutChangeListener); } FrameLayout.LayoutParams frameParams = new FrameLayout.LayoutParams( ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT ); removeAllViews(); View v = makeControllerView(); addView(v, frameParams); }
调用setPrevNextListeners
方法后,根据next
和prev
的值,在两边添加按钮。
public void setPrevNextListeners(View.OnClickListener next, View.OnClickListener prev)
显示如下
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。