本节使用系统的示例类VideoView继续SurfaceView类相关内容的讲解,以让大家能更深入理解Android系统中图形绘制基础类的实现原理。也许你会发现无法改变VideoView类的控制方面,我们可以通过重构VideoView类来实现更加个性化的播放器。

      下面是VideoView类的相关代码。

Java代码
  1. public class VideoView extends SurfaceView implements MediaPlayerControl {   
  2.     private String TAG = "VideoView";   
  3.     // settable by the client   
  4.     private Uri         mUri;   
  5.     private int         mDuration;   
  6.   
  7.     // all possible internal states   
  8.     private static final int STATE_ERROR              = -1;   
  9.     private static final int STATE_IDLE               = 0;   
  10.     private static final int STATE_PREPARING          = 1;   
  11.     private static final int STATE_PREPARED           = 2;   
  12.     private static final int STATE_PLAYING            = 3;   
  13.     private static final int STATE_PAUSED             = 4;   
  14.     private static final int STATE_PLAYBACK_COMPLETED = 5;   
  15.   
  16.     // mCurrentState is a VideoView object's current state.   
  17.     // mTargetState is the state that a method caller intends to reach.   
  18.     // For instance, regardless the VideoView object's current state,   
  19.     // calling pause() intends to bring the object to a target state   
  20.     // of STATE_PAUSED.   
  21.     private int mCurrentState = STATE_IDLE;   
  22.     private int mTargetState  = STATE_IDLE;   
  23.   
  24.     // All the stuff we need for playing and showing a video   
  25.     private SurfaceHolder mSurfaceHolder = null;   
  26.     private MediaPlayer mMediaPlayer = null;   
  27.     private int         mVideoWidth;   
  28.     private int         mVideoHeight;   
  29.     private int         mSurfaceWidth;   
  30.     private int         mSurfaceHeight;   
  31.     private MediaController mMediaController;   
  32.     private OnCompletionListener mOnCompletionListener;   
  33.     private MediaPlayer.OnPreparedListener mOnPreparedListener;   
  34.     private int         mCurrentBufferPercentage;   
  35.     private OnErrorListener mOnErrorListener;   
  36.     private int         mSeekWhenPrepared;  // recording the seek position while preparing   
  37.     private boolean     mCanPause;   
  38.     private boolean     mCanSeekBack;   
  39.     private boolean     mCanSeekForward;   
  40.   
  41.     public VideoView(Context context) {   
  42.         super(context);   
  43.         initVideoView();   
  44.     }   
  45.        
  46.     public VideoView(Context context, AttributeSet attrs) {   
  47.         this(context, attrs, 0);   
  48.         initVideoView();   
  49.     }   
  50.        
  51.     public VideoView(Context context, AttributeSet attrs, int defStyle) {   
  52.         super(context, attrs, defStyle);   
  53.         initVideoView();   
  54.     }   
  55.   
  56.     @Override  
  57.     protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {   
  58.         //Log.i("@@@@", "onMeasure");   
  59.         int width = getDefaultSize(mVideoWidth, widthMeasureSpec);   
  60.         int height = getDefaultSize(mVideoHeight, heightMeasureSpec);   
  61.         if (mVideoWidth > 0 && mVideoHeight > 0) {   
  62.             if ( mVideoWidth * height  > width * mVideoHeight ) {   
  63.                 //Log.i("@@@", "image too tall, correcting");   
  64.                 height = width * mVideoHeight / mVideoWidth;   
  65.             } else if ( mVideoWidth * height  < width * mVideoHeight ) {   
  66.                 //Log.i("@@@", "image too wide, correcting");   
  67.                 width = height * mVideoWidth / mVideoHeight;   
  68.             } else {   
  69.                 //Log.i("@@@", "aspect ratio is correct: " +   
  70.                         //width+"/"+height+"="+   
  71.                         //mVideoWidth+"/"+mVideoHeight);   
  72.             }   
  73.         }   
  74.         //Log.i("@@@@@@@@@@", "setting size: " + width + 'x' + height);   
  75.         setMeasuredDimension(width, height);   
  76.     }   
  77.        
  78.     public int resolveAdjustedSize(int desiredSize, int measureSpec) {   
  79.         int result = desiredSize;   
  80.         int specMode = MeasureSpec.getMode(measureSpec);   
  81.         int specSize =  MeasureSpec.getSize(measureSpec);   
  82.   
  83.         switch (specMode) {   
  84.             case MeasureSpec.UNSPECIFIED:   
  85.                 /* Parent says we can be as big as we want. Just don't be larger  
  86.                  * than max size imposed on ourselves.  
  87.                  */  
  88.                 result = desiredSize;   
  89.                 break;   
  90.   
  91.             case MeasureSpec.AT_MOST:   
  92.                 /* Parent says we can be as big as we want, up to specSize.   
  93.                  * Don't be larger than specSize, and don't be larger than   
  94.                  * the max size imposed on ourselves.  
  95.                  */  
  96.                 result = Math.min(desiredSize, specSize);   
  97.                 break;   
  98.                    
  99.             case MeasureSpec.EXACTLY:   
  100.                 // No choice. Do what we are told.   
  101.                 result = specSize;   
  102.                 break;   
  103.         }   
  104.         return result;   
  105. }   
  106.        
  107.     private void initVideoView() {   
  108.         mVideoWidth = 0;   
  109.         mVideoHeight = 0;   
  110.         getHolder().addCallback(mSHCallback);   
  111.         getHolder().setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);   
  112.         setFocusable(true);   
  113.         setFocusableInTouchMode(true);   
  114.         requestFocus();   
  115.         mCurrentState = STATE_IDLE;   
  116.         mTargetState  = STATE_IDLE;   
  117.     }   
  118.   
  119.     public void setVideoPath(String path) {   
  120.         setVideoURI(Uri.parse(path));   
  121.     }   
  122.   
  123.     public void setVideoURI(Uri uri) {   
  124.         mUri = uri;   
  125.         mSeekWhenPrepared = 0;   
  126.         openVideo();   
  127.         requestLayout();   
  128.         invalidate();   
  129.     }   
  130.        
  131.     public void stopPlayback() {   
  132.         if (mMediaPlayer != null) {   
  133.             mMediaPlayer.stop();   
  134.             mMediaPlayer.release();   
  135.             mMediaPlayer = null;   
  136.             mCurrentState = STATE_IDLE;   
  137.             mTargetState  = STATE_IDLE;   
  138.         }   
  139.     }   
  140.   
  141.     private void openVideo() {   
  142.         if (mUri == null || mSurfaceHolder == null) {   
  143.             // not ready for playback just yet, will try again later   
  144.             return;   
  145.         }   
  146.         // Tell the music playback service to pause    
  147.         // TODO: these constants need to be published somewhere in the framework.   
  148.         Intent i = new Intent("com.android.music.musicservicecommand");   
  149.         i.putExtra("command""pause");   
  150.         mContext.sendBroadcast(i);   
  151.   
  152.         // we shouldn't clear the target state, because somebody might have   
  153.         // called start() previously   
  154.         release(false);   
  155.         try {   
  156.             mMediaPlayer = new MediaPlayer();   
  157.             mMediaPlayer.setOnPreparedListener(mPreparedListener);   
  158.             mMediaPlayer.setOnVideoSizeChangedListener(mSizeChangedListener);   
  159.             mDuration = -1;   
  160.             mMediaPlayer.setOnCompletionListener(mCompletionListener);   
  161.             mMediaPlayer.setOnErrorListener(mErrorListener);   
  162.             mMediaPlayer.setOnBufferingUpdateListener(mBufferingUpdateListener);   
  163.             mCurrentBufferPercentage = 0;   
  164.             mMediaPlayer.setDataSource(mContext, mUri);   
  165.             mMediaPlayer.setDisplay(mSurfaceHolder);   
  166.             mMediaPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC);   
  167.             mMediaPlayer.setScreenOnWhilePlaying(true);   
  168.             mMediaPlayer.prepareAsync();   
  169.             // we don't set the target state here either, but preserve the   
  170.             // target state that was there before.   
  171.             mCurrentState = STATE_PREPARING;   
  172.             attachMediaController();   
  173.         } catch (IOException ex) {   
  174.             Log.w(TAG, "Unable to open content: " + mUri, ex);   
  175.             mCurrentState = STATE_ERROR;   
  176.             mTargetState = STATE_ERROR;   
  177.             mErrorListener.onError(mMediaPlayer, MediaPlayer.MEDIA_ERROR_UNKNOWN, 0);   
  178.             return;   
  179.         } catch (IllegalArgumentException ex) {   
  180.             Log.w(TAG, "Unable to open content: " + mUri, ex);   
  181.             mCurrentState = STATE_ERROR;   
  182.             mTargetState = STATE_ERROR;   
  183.             mErrorListener.onError(mMediaPlayer, MediaPlayer.MEDIA_ERROR_UNKNOWN, 0);   
  184.             return;   
  185.         }   
  186.     }   
  187.        
  188.     public void setMediaController(MediaController controller) {   
  189.         if (mMediaController != null) {   
  190.             mMediaController.hide();   
  191.         }   
  192.         mMediaController = controller;   
  193.         attachMediaController();   
  194.     }   
  195.   
  196.     private void attachMediaController() {   
  197.         if (mMediaPlayer != null && mMediaController != null) {   
  198.             mMediaController.setMediaPlayer(this);   
  199.             View anchorView = this.getParent() instanceof View ?   
  200.                     (View)this.getParent() : this;   
  201.             mMediaController.setAnchorView(anchorView);   
  202.             mMediaController.setEnabled(isInPlaybackState());   
  203.         }   
  204.     }   
  205.        
  206.     MediaPlayer.OnVideoSizeChangedListener mSizeChangedListener =   
  207.         new MediaPlayer.OnVideoSizeChangedListener() {   
  208.             public void onVideoSizeChanged(MediaPlayer mp, int width, int height) {   
  209.                 mVideoWidth = mp.getVideoWidth();   
  210.                 mVideoHeight = mp.getVideoHeight();   
  211.                 if (mVideoWidth != 0 && mVideoHeight != 0) {   
  212.                     getHolder().setFixedSize(mVideoWidth, mVideoHeight);   
  213.                 }   
  214.             }   
  215.     };   
  216.        
  217.     MediaPlayer.OnPreparedListener mPreparedListener = new MediaPlayer.OnPreparedListener() {   
  218.         public void onPrepared(MediaPlayer mp) {   
  219.             mCurrentState = STATE_PREPARED;   
  220.   
  221.             // Get the capabilities of the player for this stream   
  222.             Metadata data = mp.getMetadata(MediaPlayer.METADATA_ALL,   
  223.                                       MediaPlayer.BYPASS_METADATA_FILTER);   
  224.   
  225.             if (data != null) {   
  226.                 mCanPause = !data.has(Metadata.PAUSE_AVAILABLE)   
  227.                         || data.getBoolean(Metadata.PAUSE_AVAILABLE);   
  228.                 mCanSeekBack = !data.has(Metadata.SEEK_BACKWARD_AVAILABLE)   
  229.                         || data.getBoolean(Metadata.SEEK_BACKWARD_AVAILABLE);   
  230.                 mCanSeekForward = !data.has(Metadata.SEEK_FORWARD_AVAILABLE)   
  231.                         || data.getBoolean(Metadata.SEEK_FORWARD_AVAILABLE);   
  232.             } else {   
  233.                 mCanPause = mCanSeekForward = mCanSeekForward = true;   
  234.             }   
  235.   
  236.             if (mOnPreparedListener != null) {   
  237.                 mOnPreparedListener.onPrepared(mMediaPlayer);   
  238.             }   
  239.             if (mMediaController != null) {   
  240.                 mMediaController.setEnabled(true);   
  241.             }   
  242.             mVideoWidth = mp.getVideoWidth();   
  243.             mVideoHeight = mp.getVideoHeight();   
  244.   
  245.             int seekToPosition = mSeekWhenPrepared;  // mSeekWhenPrepared may be changed after seekTo() call   
  246.             if (seekToPosition != 0) {   
  247.                 seekTo(seekToPosition);   
  248.             }   
  249.             if (mVideoWidth != 0 && mVideoHeight != 0) {   
  250.                 //Log.i("@@@@", "video size: " + mVideoWidth +"/"+ mVideoHeight);   
  251.                 getHolder().setFixedSize(mVideoWidth, mVideoHeight);   
  252.                 if (mSurfaceWidth == mVideoWidth && mSurfaceHeight == mVideoHeight) {   
  253.                     // We didn't actually change the size (it was already at the size   
  254.                     // we need), so we won't get a "surface changed" callback, so   
  255.                     // start the video here instead of in the callback.   
  256.                     if (mTargetState == STATE_PLAYING) {   
  257.                         start();   
  258.                         if (mMediaController != null) {   
  259.                             mMediaController.show();   
  260.                         }   
  261.                     } else if (!isPlaying() &&   
  262.                                (seekToPosition != 0 || getCurrentPosition() > 0)) {   
  263.                        if (mMediaController != null) {   
  264.                            // Show the media controls when we're paused into a video and make 'em stick.   
  265.                            mMediaController.show(0);   
  266.                        }   
  267.                    }   
  268.                 }   
  269.             } else {   
  270.                 // We don't know the video size yet, but should start anyway.   
  271.                 // The video size might be reported to us later.   
  272.                 if (mTargetState == STATE_PLAYING) {   
  273.                     start();   
  274.                 }   
  275.             }   
  276.         }   
  277.     };   
  278.   
  279.     private MediaPlayer.OnCompletionListener mCompletionListener =   
  280.         new MediaPlayer.OnCompletionListener() {   
  281.         public void onCompletion(MediaPlayer mp) {   
  282.             mCurrentState = STATE_PLAYBACK_COMPLETED;   
  283.             mTargetState = STATE_PLAYBACK_COMPLETED;   
  284.             if (mMediaController != null) {   
  285.                 mMediaController.hide();   
  286.             }   
  287.             if (mOnCompletionListener != null) {   
  288.                 mOnCompletionListener.onCompletion(mMediaPlayer);   
  289.             }   
  290.         }   
  291.     };   
  292.   
  293.     private MediaPlayer.OnErrorListener mErrorListener =   
  294.         new MediaPlayer.OnErrorListener() {   
  295.         public boolean onError(MediaPlayer mp, int framework_err, int impl_err) {   
  296.             Log.d(TAG, "Error: " + framework_err + "," + impl_err);   
  297.             mCurrentState = STATE_ERROR;   
  298.             mTargetState = STATE_ERROR;   
  299.             if (mMediaController != null) {   
  300.                 mMediaController.hide();   
  301.             }   
  302.   
  303.             /* If an error handler has been supplied, use it and finish. */  
  304.             if (mOnErrorListener != null) {   
  305.                 if (mOnErrorListener.onError(mMediaPlayer, framework_err, impl_err)) {   
  306.                     return true;   
  307.                 }   
  308.             }   
  309.   
  310.             /* Otherwise, pop up an error dialog so the user knows that  
  311.              * something bad has happened. Only try and pop up the dialog  
  312.              * if we're attached to a window. When we're going away and no  
  313.              * longer have a window, don't bother showing the user an error.  
  314.              */  
  315.             if (getWindowToken() != null) {   
  316.                 Resources r = mContext.getResources();   
  317.                 int messageId;   
  318.   
  319.                 if (framework_err == MediaPlayer.MEDIA_ERROR_NOT_VALID_FOR_PROGRESSIVE_PLAYBACK) {   
  320.                     messageId = com.android.internal.R.string.VideoView_error_text_invalid_progressive_playback;   
  321.                 } else {   
  322.                     messageId = com.android.internal.R.string.VideoView_error_text_unknown;   
  323.                 }   
  324.   
  325.                 new AlertDialog.Builder(mContext)   
  326.                         .setTitle(com.android.internal.R.string.VideoView_error_title)   
  327.                         .setMessage(messageId)   
  328.                         .setPositiveButton(com.android.internal.R.string.VideoView_error_button,   
  329.                                 new DialogInterface.OnClickListener() {   
  330.                                     public void onClick(DialogInterface dialog, int whichButton) {   
  331.                                         /* If we get here, there is no onError listener, so  
  332.                                          * at least inform them that the video is over.  
  333.                                          */  
  334.                                         if (mOnCompletionListener != null) {   
  335.                                             mOnCompletionListener.onCompletion(mMediaPlayer);   
  336.                                         }   
  337.                                     }   
  338.                                 })   
  339.                         .setCancelable(false)   
  340.                         .show();   
  341.             }   
  342.             return true;   
  343.         }   
  344.     };   
  345.   
  346.     private MediaPlayer.OnBufferingUpdateListener mBufferingUpdateListener =   
  347.         new MediaPlayer.OnBufferingUpdateListener() {   
  348.         public void onBufferingUpdate(MediaPlayer mp, int percent) {   
  349.             mCurrentBufferPercentage = percent;   
  350.         }   
  351.     };   
  352.   
  353.     /**  
  354.      * Register a callback to be invoked when the media file  
  355.      * is loaded and ready to go.  
  356.      *  
  357.      * @param l The callback that will be run  
  358.      */  
  359.     public void setOnPreparedListener(MediaPlayer.OnPreparedListener l)   
  360.     {   
  361.         mOnPreparedListener = l;   
  362.     }   
  363.   
  364.     /**  
  365.      * Register a callback to be invoked when the end of a media file  
  366.      * has been reached during playback.  
  367.      *  
  368.      * @param l The callback that will be run  
  369.      */  
  370.     public void setOnCompletionListener(OnCompletionListener l)   
  371.     {   
  372.         mOnCompletionListener = l;   
  373.     }   
  374.   
  375.     /**  
  376.      * Register a callback to be invoked when an error occurs  
  377.      * during playback or setup.  If no listener is specified,  
  378.      * or if the listener returned false, VideoView will inform  
  379.      * the user of any errors.  
  380.      *  
  381.      * @param l The callback that will be run  
  382.      */  
  383.     public void setOnErrorListener(OnErrorListener l)   
  384.     {   
  385.         mOnErrorListener = l;   
  386.     }   
  387.   
  388.     SurfaceHolder.Callback mSHCallback = new SurfaceHolder.Callback()   
  389.     {   
  390.         public void surfaceChanged(SurfaceHolder holder, int format,   
  391.                                     int w, int h)   
  392.         {   
  393.             mSurfaceWidth = w;   
  394.             mSurfaceHeight = h;   
  395.             boolean isValidState =  (mTargetState == STATE_PLAYING);   
  396.             boolean hasValidSize = (mVideoWidth == w && mVideoHeight == h);   
  397.             if (mMediaPlayer != null && isValidState && hasValidSize) {   
  398.                 if (mSeekWhenPrepared != 0) {   
  399.                     seekTo(mSeekWhenPrepared);   
  400.                 }   
  401.                 start();   
  402.                 if (mMediaController != null) {   
  403.                     mMediaController.show();   
  404.                 }   
  405.             }   
  406.         }   
  407.   
  408.         public void surfaceCreated(SurfaceHolder holder)   
  409.         {   
  410.             mSurfaceHolder = holder;   
  411.             openVideo();   
  412.         }   
  413.   
  414.         public void surfaceDestroyed(SurfaceHolder holder)   
  415.         {   
  416.             // after we return from this we can't use the surface any more   
  417.             mSurfaceHolder = null;   
  418.             if (mMediaController != null) mMediaController.hide();   
  419.             release(true);   
  420.         }   
  421.     };   
  422.   
  423.     /*  
  424.      * release the media player in any state  
  425.      */  
  426.     private void release(boolean cleartargetstate) {   
  427.         if (mMediaPlayer != null) {   
  428.             mMediaPlayer.reset();   
  429.             mMediaPlayer.release();   
  430.             mMediaPlayer = null;   
  431.             mCurrentState = STATE_IDLE;   
  432.             if (cleartargetstate) {   
  433.                 mTargetState  = STATE_IDLE;   
  434.             }   
  435.         }   
  436.     }   
  437.   
  438.     @Override  
  439.     public boolean onTouchEvent(MotionEvent ev) {   
  440.         if (isInPlaybackState() && mMediaController != null) {   
  441.             toggleMediaControlsVisiblity();   
  442.         }   
  443.         return false;   
  444.     }   
  445.        
  446.     @Override  
  447.     public boolean onTrackballEvent(MotionEvent ev) {   
  448.         if (isInPlaybackState() && mMediaController != null) {   
  449.             toggleMediaControlsVisiblity();   
  450.         }   
  451.         return false;   
  452.     }   
  453.        
  454.     @Override  
  455.     public boolean onKeyDown(int keyCode, KeyEvent event)   
  456.     {   
  457.         boolean isKeyCodeSupported = keyCode != KeyEvent.KEYCODE_BACK &&   
  458.                                      keyCode != KeyEvent.KEYCODE_VOLUME_UP &&   
  459.                                      keyCode != KeyEvent.KEYCODE_VOLUME_DOWN &&   
  460.                                      keyCode != KeyEvent.KEYCODE_MENU &&   
  461.                                      keyCode != KeyEvent.KEYCODE_CALL &&   
  462.                                      keyCode != KeyEvent.KEYCODE_ENDCALL;   
  463.         if (isInPlaybackState() && isKeyCodeSupported && mMediaController != null) {   
  464.             if (keyCode == KeyEvent.KEYCODE_HEADSETHOOK ||   
  465.                     keyCode == KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE) {   
  466.                 if (mMediaPlayer.isPlaying()) {   
  467.                     pause();   
  468.                     mMediaController.show();   
  469.                 } else {   
  470.                     start();   
  471.                     mMediaController.hide();   
  472.                 }   
  473.                 return true;   
  474.             } else if (keyCode == KeyEvent.KEYCODE_MEDIA_STOP    
  475.                     && mMediaPlayer.isPlaying()) {   
  476.                 pause();   
  477.                 mMediaController.show();   
  478.             } else {   
  479.                 toggleMediaControlsVisiblity();   
  480.             }   
  481.         }   
  482.   
  483.         return super.onKeyDown(keyCode, event);   
  484.     }   
  485.   
  486.     private void toggleMediaControlsVisiblity() {   
  487.         if (mMediaController.isShowing()) {    
  488.             mMediaController.hide();   
  489.         } else {   
  490.             mMediaController.show();   
  491.         }   
  492.     }   
  493.        
  494.     public void start() {   
  495.         if (isInPlaybackState()) {   
  496.             mMediaPlayer.start();   
  497.             mCurrentState = STATE_PLAYING;   
  498.         }   
  499.         mTargetState = STATE_PLAYING;   
  500.     }   
  501.        
  502.     public void pause() {   
  503.         if (isInPlaybackState()) {   
  504.             if (mMediaPlayer.isPlaying()) {   
  505.                 mMediaPlayer.pause();   
  506.                 mCurrentState = STATE_PAUSED;   
  507.             }   
  508.         }   
  509.         mTargetState = STATE_PAUSED;   
  510.     }   
  511.        
  512.     // cache duration as mDuration for faster access   
  513.     public int getDuration() {   
  514.         if (isInPlaybackState()) {   
  515.             if (mDuration > 0) {   
  516.                 return mDuration;   
  517.             }   
  518.             mDuration = mMediaPlayer.getDuration();   
  519.             return mDuration;   
  520.         }   
  521.         mDuration = -1;   
  522.         return mDuration;   
  523.     }   
  524.        
  525.     public int getCurrentPosition() {   
  526.         if (isInPlaybackState()) {   
  527.             return mMediaPlayer.getCurrentPosition();   
  528.         }   
  529.         return 0;   
  530.     }   
  531.        
  532.     public void seekTo(int msec) {   
  533.         if (isInPlaybackState()) {   
  534.             mMediaPlayer.seekTo(msec);   
  535.             mSeekWhenPrepared = 0;   
  536.         } else {   
  537.             mSeekWhenPrepared = msec;   
  538.         }   
  539.     }       
  540.                
  541.     public boolean isPlaying() {   
  542.         return isInPlaybackState() && mMediaPlayer.isPlaying();   
  543.     }   
  544.        
  545.     public int getBufferPercentage() {   
  546.         if (mMediaPlayer != null) {   
  547.             return mCurrentBufferPercentage;   
  548.         }   
  549.         return 0;   
  550.     }   
  551.   
  552.     private boolean isInPlaybackState() {   
  553.         return (mMediaPlayer != null &&   
  554.                 mCurrentState != STATE_ERROR &&   
  555.                 mCurrentState != STATE_IDLE &&   
  556.                 mCurrentState != STATE_PREPARING);   
  557.     }   
  558.   
  559.     public boolean canPause() {   
  560.         return mCanPause;   
  561.     }   
  562.   
  563.     public boolean canSeekBackward() {   
  564.         return mCanSeekBack;   
  565.     }   
  566.   
  567.     public boolean canSeekForward() {   
  568.         return mCanSeekForward;   
  569.     }   
  570. }  

 

本文发布:Android开发网
本文地址:http://www.teaching4real.com/android/game/85.html
2012年6月23日
发布:鸡啄米 分类:Android游戏开发 浏览: 注册送白菜网:0