Android VideoView类实例讲解
程序员文章站
2024-03-06 19:50:44
本节使用系统的示例类videoview继续surfaceview类相关内容的...
本节使用系统的示例类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 类的详细介绍,后续继续补充相关知识,谢谢大家对本站的支持!