Android VideoView类实例讲解
本节使用系统的示例类VideoView继续SurfaceView类相关内容的讲解,以让大家能更深入理解Android系统中图形绘制基础类的实现原理。也许你会发现无法改变VideoView类的控制方面,我们可以通过重构VideoView类来实现更加个性化的播放器。
下面是VideoView类的相关代码。
Java代码
publicclassVideoViewextendsSurfaceViewimplementsMediaPlayerControl{
privateStringTAG="VideoView";
//settablebytheclient
privateUrimUri;
privateintmDuration;
//allpossibleinternalstates
privatestaticfinalintSTATE_ERROR=-1;
privatestaticfinalintSTATE_IDLE=0;
privatestaticfinalintSTATE_PREPARING=1;
privatestaticfinalintSTATE_PREPARED=2;
privatestaticfinalintSTATE_PLAYING=3;
privatestaticfinalintSTATE_PAUSED=4;
privatestaticfinalintSTATE_PLAYBACK_COMPLETED=5;
//mCurrentStateisaVideoViewobject'scurrentstate.
//mTargetStateisthestatethatamethodcallerintendstoreach.
//Forinstance,regardlesstheVideoViewobject'scurrentstate,
//callingpause()intendstobringtheobjecttoatargetstate
//ofSTATE_PAUSED.
privateintmCurrentState=STATE_IDLE;
privateintmTargetState=STATE_IDLE;
//Allthestuffweneedforplayingandshowingavideo
privateSurfaceHoldermSurfaceHolder=null;
privateMediaPlayermMediaPlayer=null;
privateintmVideoWidth;
privateintmVideoHeight;
privateintmSurfaceWidth;
privateintmSurfaceHeight;
privateMediaControllermMediaController;
privateOnCompletionListenermOnCompletionListener;
privateMediaPlayer.OnPreparedListenermOnPreparedListener;
privateintmCurrentBufferPercentage;
privateOnErrorListenermOnErrorListener;
privateintmSeekWhenPrepared;//recordingtheseekpositionwhilepreparing
privatebooleanmCanPause;
privatebooleanmCanSeekBack;
privatebooleanmCanSeekForward;
publicVideoView(Contextcontext){
super(context);
initVideoView();
}
publicVideoView(Contextcontext,AttributeSetattrs){
this(context,attrs,0);
initVideoView();
}
publicVideoView(Contextcontext,AttributeSetattrs,intdefStyle){
super(context,attrs,defStyle);
initVideoView();
}
@Override
protectedvoidonMeasure(intwidthMeasureSpec,intheightMeasureSpec){
//Log.i("@@@@","onMeasure");
intwidth=getDefaultSize(mVideoWidth,widthMeasureSpec);
intheight=getDefaultSize(mVideoHeight,heightMeasureSpec);
if(mVideoWidth>0&&mVideoHeight>0){
if(mVideoWidth*height>width*mVideoHeight){
//Log.i("@@@","imagetootall,correcting");
height=width*mVideoHeight/mVideoWidth;
}elseif(mVideoWidth*height<width*mVideoHeight){
//Log.i("@@@","imagetoowide,correcting");
width=height*mVideoWidth/mVideoHeight;
}else{
//Log.i("@@@","aspectratioiscorrect:"+
//width+"/"+height+"="+
//mVideoWidth+"/"+mVideoHeight);
}
}
//Log.i("@@@@@@@@@@","settingsize:"+width+'x'+height);
setMeasuredDimension(width,height);
}
publicintresolveAdjustedSize(intdesiredSize,intmeasureSpec){
intresult=desiredSize;
intspecMode=MeasureSpec.getMode(measureSpec);
intspecSize=MeasureSpec.getSize(measureSpec);
switch(specMode){
caseMeasureSpec.UNSPECIFIED:
result=desiredSize;
break;
caseMeasureSpec.AT_MOST:
/*Parentsayswecanbeasbigaswewant,uptospecSize.
*Don'tbelargerthanspecSize,anddon'tbelargerthan
*themaxsizeimposedonourselves.
*/
result=Math.min(desiredSize,specSize);
break;
caseMeasureSpec.EXACTLY:
//Nochoice.Dowhatwearetold.
result=specSize;
break;
}
returnresult;
}
privatevoidinitVideoView(){
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;
}
publicvoidsetVideoPath(Stringpath){
setVideoURI(Uri.parse(path));
}
publicvoidsetVideoURI(Uriuri){
mUri=uri;
mSeekWhenPrepared=0;
openVideo();
requestLayout();
invalidate();
}
publicvoidstopPlayback(){
if(mMediaPlayer!=null){
mMediaPlayer.stop();
mMediaPlayer.release();
mMediaPlayer=null;
mCurrentState=STATE_IDLE;
mTargetState=STATE_IDLE;
}
}
privatevoidopenVideo(){
if(mUri==null||mSurfaceHolder==null){
//notreadyforplaybackjustyet,willtryagainlater
return;
}
//Tellthemusicplaybackservicetopause
//TODO:theseconstantsneedtobepublishedsomewhereintheframework.
Intenti=newIntent("com.android.music.musicservicecommand");
i.putExtra("command","pause");
mContext.sendBroadcast(i);
//weshouldn'tclearthetargetstate,becausesomebodymighthave
//calledstart()previously
release(false);
try{
mMediaPlayer=newMediaPlayer();
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();
//wedon'tsetthetargetstatehereeither,butpreservethe
//targetstatethatwastherebefore.
mCurrentState=STATE_PREPARING;
attachMediaController();
}catch(IOExceptionex){
Log.w(TAG,"Unabletoopencontent:"+mUri,ex);
mCurrentState=STATE_ERROR;
mTargetState=STATE_ERROR;
mErrorListener.onError(mMediaPlayer,MediaPlayer.MEDIA_ERROR_UNKNOWN,0);
return;
}catch(IllegalArgumentExceptionex){
Log.w(TAG,"Unabletoopencontent:"+mUri,ex);
mCurrentState=STATE_ERROR;
mTargetState=STATE_ERROR;
mErrorListener.onError(mMediaPlayer,MediaPlayer.MEDIA_ERROR_UNKNOWN,0);
return;
}
}
publicvoidsetMediaController(MediaControllercontroller){
if(mMediaController!=null){
mMediaController.hide();
}
mMediaController=controller;
attachMediaController();
}
privatevoidattachMediaController(){
if(mMediaPlayer!=null&&mMediaController!=null){
mMediaController.setMediaPlayer(this);
ViewanchorView=this.getParent()instanceofView?
(View)this.getParent():this;
mMediaController.setAnchorView(anchorView);
mMediaController.setEnabled(isInPlaybackState());
}
}
MediaPlayer.OnVideoSizeChangedListenermSizeChangedListener=
newMediaPlayer.OnVideoSizeChangedListener(){
publicvoidonVideoSizeChanged(MediaPlayermp,intwidth,intheight){
mVideoWidth=mp.getVideoWidth();
mVideoHeight=mp.getVideoHeight();
if(mVideoWidth!=0&&mVideoHeight!=0){
getHolder().setFixedSize(mVideoWidth,mVideoHeight);
}
}
};
MediaPlayer.OnPreparedListenermPreparedListener=newMediaPlayer.OnPreparedListener(){
publicvoidonPrepared(MediaPlayermp){
mCurrentState=STATE_PREPARED;
//Getthecapabilitiesoftheplayerforthisstream
Metadatadata=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();
intseekToPosition=mSeekWhenPrepared;//mSeekWhenPreparedmaybechangedafterseekTo()call
if(seekToPosition!=0){
seekTo(seekToPosition);
}
if(mVideoWidth!=0&&mVideoHeight!=0){
//Log.i("@@@@","videosize:"+mVideoWidth+"/"+mVideoHeight);
getHolder().setFixedSize(mVideoWidth,mVideoHeight);
if(mSurfaceWidth==mVideoWidth&&mSurfaceHeight==mVideoHeight){
//Wedidn'tactuallychangethesize(itwasalreadyatthesize
//weneed),sowewon'tgeta"surfacechanged"callback,so
//startthevideohereinsteadofinthecallback.
if(mTargetState==STATE_PLAYING){
start();
if(mMediaController!=null){
mMediaController.show();
}
}elseif(!isPlaying()&&
(seekToPosition!=0||getCurrentPosition()>0)){
if(mMediaController!=null){
//Showthemediacontrolswhenwe'repausedintoavideoandmake'emstick.
mMediaController.show(0);
}
}
}
}else{
//Wedon'tknowthevideosizeyet,butshouldstartanyway.
//Thevideosizemightbereportedtouslater.
if(mTargetState==STATE_PLAYING){
start();
}
}
}
};
privateMediaPlayer.OnCompletionListenermCompletionListener=
newMediaPlayer.OnCompletionListener(){
publicvoidonCompletion(MediaPlayermp){
mCurrentState=STATE_PLAYBACK_COMPLETED;
mTargetState=STATE_PLAYBACK_COMPLETED;
if(mMediaController!=null){
mMediaController.hide();
}
if(mOnCompletionListener!=null){
mOnCompletionListener.onCompletion(mMediaPlayer);
}
}
};
privateMediaPlayer.OnErrorListenermErrorListener=
newMediaPlayer.OnErrorListener(){
publicbooleanonError(MediaPlayermp,intframework_err,intimpl_err){
Log.d(TAG,"Error:"+framework_err+","+impl_err);
mCurrentState=STATE_ERROR;
mTargetState=STATE_ERROR;
if(mMediaController!=null){
mMediaController.hide();
}
/*Ifanerrorhandlerhasbeensupplied,useitandfinish.*/
if(mOnErrorListener!=null){
if(mOnErrorListener.onError(mMediaPlayer,framework_err,impl_err)){
returntrue;
}
}
/*Otherwise,popupanerrordialogsotheuserknowsthat
*somethingbadhashappened.Onlytryandpopupthedialog
*ifwe'reattachedtoawindow.Whenwe'regoingawayandno
*longerhaveawindow,don'tbothershowingtheuseranerror.
*/
if(getWindowToken()!=null){
Resourcesr=mContext.getResources();
intmessageId;
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;
}
newAlertDialog.Builder(mContext)
.setTitle(com.android.internal.R.string.VideoView_error_title)
.setMessage(messageId)
.setPositiveButton(com.android.internal.R.string.VideoView_error_button,
newDialogInterface.OnClickListener(){
publicvoidonClick(DialogInterfacedialog,intwhichButton){
/*Ifwegethere,thereisnoonErrorlistener,so
*atleastinformthemthatthevideoisover.
*/
if(mOnCompletionListener!=null){
mOnCompletionListener.onCompletion(mMediaPlayer);
}
}
})
.setCancelable(false)
.show();
}
returntrue;
}
};
privateMediaPlayer.OnBufferingUpdateListenermBufferingUpdateListener=
newMediaPlayer.OnBufferingUpdateListener(){
publicvoidonBufferingUpdate(MediaPlayermp,intpercent){
mCurrentBufferPercentage=percent;
}
};
/**
*Registeracallbacktobeinvokedwhenthemediafile
*isloadedandreadytogo.
*
*@paramlThecallbackthatwillberun
*/
publicvoidsetOnPreparedListener(MediaPlayer.OnPreparedListenerl)
{
mOnPreparedListener=l;
}
/**
*Registeracallbacktobeinvokedwhentheendofamediafile
*hasbeenreachedduringplayback.
*
*@paramlThecallbackthatwillberun
*/
publicvoidsetOnCompletionListener(OnCompletionListenerl)
{
mOnCompletionListener=l;
}
/**
*Registeracallbacktobeinvokedwhenanerroroccurs
*duringplaybackorsetup.Ifnolistenerisspecified,
*orifthelistenerreturnedfalse,VideoViewwillinform
*theuserofanyerrors.
*
*@paramlThecallbackthatwillberun
*/
publicvoidsetOnErrorListener(OnErrorListenerl)
{
mOnErrorListener=l;
}
SurfaceHolder.CallbackmSHCallback=newSurfaceHolder.Callback()
{
publicvoidsurfaceChanged(SurfaceHolderholder,intformat,
intw,inth)
{
mSurfaceWidth=w;
mSurfaceHeight=h;
booleanisValidState=(mTargetState==STATE_PLAYING);
booleanhasValidSize=(mVideoWidth==w&&mVideoHeight==h);
if(mMediaPlayer!=null&&isValidState&&hasValidSize){
if(mSeekWhenPrepared!=0){
seekTo(mSeekWhenPrepared);
}
start();
if(mMediaController!=null){
mMediaController.show();
}
}
}
publicvoidsurfaceCreated(SurfaceHolderholder)
{
mSurfaceHolder=holder;
openVideo();
}
publicvoidsurfaceDestroyed(SurfaceHolderholder)
{
//afterwereturnfromthiswecan'tusethesurfaceanymore
mSurfaceHolder=null;
if(mMediaController!=null)mMediaController.hide();
release(true);
}
};
privatevoidrelease(booleancleartargetstate){
if(mMediaPlayer!=null){
mMediaPlayer.reset();
mMediaPlayer.release();
mMediaPlayer=null;
mCurrentState=STATE_IDLE;
if(cleartargetstate){
mTargetState=STATE_IDLE;
}
}
}
@Override
publicbooleanonTouchEvent(MotionEventev){
if(isInPlaybackState()&&mMediaController!=null){
toggleMediaControlsVisiblity();
}
returnfalse;
}
@Override
publicbooleanonTrackballEvent(MotionEventev){
if(isInPlaybackState()&&mMediaController!=null){
toggleMediaControlsVisiblity();
}
returnfalse;
}
@Override
publicbooleanonKeyDown(intkeyCode,KeyEventevent)
{
booleanisKeyCodeSupported=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();
}
returntrue;
}elseif(keyCode==KeyEvent.KEYCODE_MEDIA_STOP
&&mMediaPlayer.isPlaying()){
pause();
mMediaController.show();
}else{
toggleMediaControlsVisiblity();
}
}
returnsuper.onKeyDown(keyCode,event);
}
privatevoidtoggleMediaControlsVisiblity(){
if(mMediaController.isShowing()){
mMediaController.hide();
}else{
mMediaController.show();
}
}
publicvoidstart(){
if(isInPlaybackState()){
mMediaPlayer.start();
mCurrentState=STATE_PLAYING;
}
mTargetState=STATE_PLAYING;
}
publicvoidpause(){
if(isInPlaybackState()){
if(mMediaPlayer.isPlaying()){
mMediaPlayer.pause();
mCurrentState=STATE_PAUSED;
}
}
mTargetState=STATE_PAUSED;
}
//cachedurationasmDurationforfasteraccess
publicintgetDuration(){
if(isInPlaybackState()){
if(mDuration>0){
returnmDuration;
}
mDuration=mMediaPlayer.getDuration();
returnmDuration;
}
mDuration=-1;
returnmDuration;
}
publicintgetCurrentPosition(){
if(isInPlaybackState()){
returnmMediaPlayer.getCurrentPosition();
}
return0;
}
publicvoidseekTo(intmsec){
if(isInPlaybackState()){
mMediaPlayer.seekTo(msec);
mSeekWhenPrepared=0;
}else{
mSeekWhenPrepared=msec;
}
}
publicbooleanisPlaying(){
returnisInPlaybackState()&&mMediaPlayer.isPlaying();
}
publicintgetBufferPercentage(){
if(mMediaPlayer!=null){
returnmCurrentBufferPercentage;
}
return0;
}
privatebooleanisInPlaybackState(){
return(mMediaPlayer!=null&&
mCurrentState!=STATE_ERROR&&
mCurrentState!=STATE_IDLE&&
mCurrentState!=STATE_PREPARING);
}
publicbooleancanPause(){
returnmCanPause;
}
publicbooleancanSeekBackward(){
returnmCanSeekBack;
}
publicbooleancanSeekForward(){
returnmCanSeekForward;
}
}
以上就是对AndroidVideoView类的详细介绍,后续继续补充相关知识,谢谢大家对本站的支持!