Android Dev:VideoView源代码浅析及拓展应用

做Android开发不免要涉及到编写媒体播放器,对于初学者来说用MediaPlayer实现一个具有基本功能的播放器(有进度条,可以通过进度条上的按钮进行控制)还是有一定难度的,幸好Android还提供了一个VideoView类,借用该类可以快速实现简单的媒体播放功能,其源代码如下(单击右边那个箭头展开):

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

VideoView这个类写得非常漂亮,尤其是它的实行机制。在第58-67行定义了一些常量,通过变量名可以看出这些常量表示的是一些状态,在第74和75行分别有两个变量mCurrnetState和mTargetState,这两个变量的分别用来记录当前的状态,和目标状态。VideoView类就是围绕这这两个状态来进行相应的操作,通读VideoView源代码我们可以很清楚地认识到这一点,同时我们也能发现Google显然考虑了播放多媒体文件时的各种情况以保证播放的顺利进行,虽然本文题目是VideoView浅析,但是我不想对该类做很多讲解,一是我还不能完全理解每个部分的具体作用,二是这个类写得并不复杂,细心多读几遍很容易就能了解个大概,所以希望各位在往下阅读前先通读一遍VideoView的源代码。

如果要实现简单的播放器我们直接调用这个类就可以了,但是要对这个类做一些修改来满足我们的需求首先要做的就是把这个类变成我们“自己的”。新建一个Class,取名为MyVideoView,然后就是把VideoView源代码Copy下来,覆盖原来的MyVideoView类,此时肯定会有很多错误,按照下列方法解决(注意最好先自己查找错误再在下面的修改方法中查找对应的解决方法):

  1. 如果你没有更改类名,把类名更改为MyVideoView
  2. 更改三个构造函数名为MyVideoView
  3. openVideo()中显示“mContext cannot be resolved”。在变量定义部分添加一个:private Context mContext; 并在三个构造函数中加上:mContext = context;。关于Context,如果不理解请自行解决。
  4. openVideo()中,mMediaPlayer.setDataSource(mContext, mUri, mHeaders);错误,通过源代码可以看出在三个参数的setDataSource上面的注释中有这样一句:@hide pending API council,表示这个方法还没有开放,我们只能使用两个参数的版本,所以把这一句改为:mMediaPlayer.setDataSource(mContext, mUri);
  5. onPrepared(MediaPlayer mp)中有大量关于Metadata的错误,而又无法查看到Metadata类的源代码,我不了解其具体实现方法(有高手知道的话还请指点),但是我们可以分析出这一段是在判断相应的媒体文件播放时能否暂停、快进/退,暂时就把整个有错误的部分替换成:mCanPause = mCanSeekBack = mCanSeekForward = true;,即默认媒体文件可以支持暂停等功能。
  6. onError(MediaPlayer mp, int framework_err, int impl_err) 中的两个messageId,这两个显然是出错时的错误代码,我没有查找其具体值,暂时分别改为123和456(不要笑,希望有能力的去找一下这两个值具体是什么)。
  7. onError(MediaPlayer mp, int framework_err, int impl_err) 中的setTitle和setPositiveButton后面的参数我也没有去找具体的字符串是什么,暂时就分别改为"error!!!"和"OK"。
  8. suspend()中的mMediaPlayer.suspend()错误,同样是因为@hide的问题,由于这是一个public的函数,VideoView的其他部分也没有调用这个函数,处于方便,直接将其注释掉,如有需要,我们要自己实现这一功能。
  9. resume() 中同8.一样的问题,没办法这个我们也先注释了,另外在surfaceCreated(SurfaceHolder holder)中调用了这个函数,所以我们只保留surfaceCreated中else部分。

修改后的代码如下(单击右边那个箭头展开):

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

 

现在MyVideoView中已经没有任何错误了,我们可以直接像VideoView那样使用了,至于XML部分只需将VideoView替换为你自己的类的,如com.XXX.XXX.MyVdieoView。如果需要进行一些修改也变得很容易了,比如我们要实现这样一个功能:播放音频文件时显示一张图片,点击图片可以显示控制条并可进行相应操作。

新建一个Class取名为MyAudioView,把继承由原来的SurfaceView改为ImageView,代码如下(单击右边那个箭头展开):

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

 

主要的修改就是删除了与视频长宽以及SurfaceView有关的东西,大家可以自行对比找出修改的部分,修改之后就可以像ImageView一样给MyAudioView设置一个图片,同时还可以实现播放音频的功能。

 

PS. 本文内容仅供参考学习,同时难免有所疏漏,如有问题还请指教。同时给初学者一个建议,如果你的开发环境还不能查看Android的源代码,强烈建议你实现这个功能(具体方法不说了,一搜一大堆),源代码毕竟是那些高手中的高手写的,有许多值得我们学习借鉴的地方,同时也可以用类似本文提到的方法,借用源代码快书实现一些我们想要的功能。