欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  移动技术

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