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,>

继续阅读Android Dev:VideoView源代码浅析及拓展应用

解决Mac OS不能更新,下载更新速度慢的方法

更新Again:80.67.74.160这个ip好像已经ping不通了,如果之前修改了hosts,删除hosts中添加的那一行就行,更新应该也正常了。教育网内速度当然就又慢下来,正在实验这个ip是否能快点60.254.175.32,以下是dig出来的ip,仅供参考

60.254.175.23

60.254.175.22

125.56.202.7

125.56.202.12

125.56.202.12

121.248.156.7

—————————————————————————

更新:似乎在教育网内好用,教育网外可能反而无法更新

不知道用Mac的各位有没有遇到这两种情况:

  1. 打开iTunes/iWork,提示有新版本,点击升级,跳转到软件更新程序,检查了半天居然说你的软件是最新版。
  2. 下载更新速度奇慢,估计也就几K,一个更新能下一天,有时还有错误要重新下载。

如果你符合以上两种情况那么只需要执行一下几步就可以轻松解决

  1. 打开终端,sudo /Applications/TextEdit.app/Contents/MacOS/TextEdit /etc/hosts,然后可能有提示要输入你的密码,TextEdit会打开hosts文件让你编辑
  2. 在这个文件里添加一行:80.67.74.160 swcdn.apple.com 注意保存

就这么简单,之后更新就应该恢复正常了,下载速度也比原来强百倍,如果想知道更多细节参见这篇文章

PS:这个方法应该早就有了,我用了这么长时间mac居然没发现,真是罪过啊!

Mac OS 下命令行编辑plist的方法

    一般我们编辑plist文件都是直接打开,用PlistEditor什么的进行可视化编辑,但是如果想用脚本编辑plist文件就要用到命令行了,其实这是一个很简单的命令:defaults

    defaults 命令的帮助如下

'defaults' [-currentHost | -host ] followed by one of the following:

  read                                 shows all defaults
  read                         shows defaults for given domain
  read                    shows defaults for given domain, key

  read-type               shows the type for the given domain, key

  write            writes domain (overwrites existing)
  write            writes key for domain

  rename 
   renames old_key to new_key

  delete                       deletes domain
  delete                  deletes key in domain

  domains                              lists all domains
  find                           lists all entries containing word
  help                                 print this help

 is (  | -app  | -globalDomain )
         or a path to a file omitting the '.plist' extension

 is one of:
  
  -string 
  -data 
  -int[eger] 
  -float  
  -bool[ean] (true | false | yes | no)
  -date 
  -array   ...
  -array-add   ...
  -dict     ...
  -dict-add  

 

    光看这些就大概知道怎么弄了吧,下面是几个例子:

defaults read com.xxx.xxx // 输出文件中所有信息
defaults write com.xxx.xxx   // 改变某个key的value,如果没有该key则添�

 

    注意:在 "com.xxx.xxx" 之后没有 ".plist",开始我犯了这个错误,弄半天不知道为何plist文件中的值没有改变。

   (END)

[转载]Unix-Center.Net需要您的帮助

以下内容是原文,推荐直接访问原网站:


我恳请诸位花一点时间读完这篇文章,因为将有数以万计的人会从您的爱心中得到帮助。

Unix-Center.Net的目标是为研究、学习和使用各种版本的Unix和类Unix操作系统的教师、学生和工程技术人员提供一个体验和测试各种版本的Unix和类Unix系统的软硬件平台。该平台能够为所有注册用户免费提供SSH/VNC服务,MySQL数据库服务,传统的C/C++、Java、Fortran等多种语言开发环境,基于Apache、MySQL和PHP的Web应用开发环境。简单地讲,Unix-Center.Net的注册用户可以远程登录进入多个不同的操作系统,具备自己独立的用户空间和磁盘配额,享受该操作系统上普通用户的所有权限,学习和使用各种版本的Unix和类Unix操作系统的常用命令和功能,可以将自己正在开发的应用程序上载到Unix体验中心的服务器,在不同的软硬件平台上编译和运行。

到目前为止,Unix-Center.Net所提供的操作系统包括AIX 5.3,Solaris 10,OpenSolaris 2009.06,Fedora Core 10,Ubuntu 8.04,FreeBSD 6.2,Debian Linux for MIPS。这些操作系统分别运行在IBM Power 5,Sun UltraSPARC T1,AMD Opteron,Intel Xeon,龙芯2E等不同构架的处理器上。各种各样的服务器,再加上交换机、防火墙、存储等等,Unix-Center.Net的全部设备需要整整三个机柜才能够装得下。

继续阅读[转载]Unix-Center.Net需要您的帮助

修改Mac下粘贴并匹配样式的快捷键

    Mac下的粘贴功能很“智能”,可以把原来的格式文字原封不动的搬过来,但这样也照成一些麻烦,如乱码,格式混乱等,Mac下还有另一种粘贴方式即“粘贴并匹配样式”(Paste And Match Sytle),这种粘贴方式所”匹配“的样式不是原文的样式,而是要粘贴到的位置的样式,但这种粘贴方法要同时按下“⇧⌘⌥V”这四个按键,实在是有点困难,而对我来讲还是这种粘贴方式更加符合我的工作习惯。解决方法很明确,就是将两种粘贴方式的快捷键互换一下,这样能很好的保证不会与其他快捷键有冲突,方法如下:

  1. 打开 偏好设置>>键盘>>键盘快捷键
  2. 在左边的列表里选择“应用程序快捷键”,然后点下面那个“+”号添加一项
  3. 在弹出的对话框中,“应用程序”选则你要修改的程序,尽量不要选择全部程序,因为某些程序没有匹配样式并粘贴这个操作(比如终端,Mac QQ等),这样就会导致粘贴时必须按⇧⌘⌥+V才行(如果一定要选择所有程序,要么就单独为那些出问题的程序把粘贴这一操作的快捷键设置成⌘+v,要么就使用别的快捷键),“菜单标题”输入“粘贴”,“键盘快捷键”⇧⌘⌥+v,点添加。
  4. 再用相同的方法添加“粘贴并匹配样式”,快捷键“⌘+v”,如果某些英文软件不支持中文,还要添加“Paste”和“Paste And Match Style",方法类似。

    OK,修改完成,现在可以开个程序试试修改的效果了。(END)

Apple岁末广告攻势

Apple这样一家大公司之所以如此成功,当然少不了广告的作用,下面是笔者在2010年年末收到的apple广告邮件截图,与大家分享一下,共同体验Apple的强大。

这一段的广告与往日不同,采用了红色的文字和按钮,完美展现出节日的气氛。

11月16日,应该是这一波广告攻势的第一封mail吧,涵盖了Apple2010年的主要产品:iPad、Air、Touch、iPhone、Apple TV

继续阅读Apple岁末广告攻势

Mac OS X Snow Leopard 64bit下使用ipv6的方法

    需要一个软件Teredo(Miredo), 安装后在系统偏好设置里, 下载地址

    Teredo我就不介绍了, 如果真的不知道就自己查下吧, 这篇文章主要解决64bit下Teredo不能获得ipv6地址的问题. 可以额外参考这篇文章(EN).

   这个版本的Teredo只能工作在32bit下, 64bit无法获得ipv6地址, 现象是Teredo设置界面Teredo Tunnel那一项后面那个圆点(指示灯)总不是绿的(好像是黄的, 如果关闭Teredo后是红的), 高手可以尝试下载源代码自行编译修改. 懒人可以接着往下看解决方法:

    这里需要安装另一个东西–TunTap , 到这里下载, 我提供的地址是20090913版的,如果有更新请在附近查找, 安装后可以发现Teredo设置里面上面提到的指示灯变绿色了, 下面的Teredo Address也有信息了, 是你获取的ipv6地址, 如果还是不行试试把那个server换了, 微软那个我这里不能连接上,  teredo.remlab.net目前好用. 如果还是不行, 请留言. (END)

更新几个服务器:

 

Public teredo servers:

   * teredo.remlab.net / teredo-debian.remlab.net (France)

   * teredo.autotrans.consulintel.com (Spain)

   * teredo.ipv6.microsoft.com (USA, Redmond) (default for WindowsXP/2003/Vista/2008 OS)

   * teredo.ngix.ne.kr (South Korea)

   * teredo.managemydedi.com (USA, Chicago)

   * teredo.trex.fi (Finland)

 

Godaddy免费空间发送邮件方法

         原文是英文版,还被墙了,我在这转载并翻译一下,不能保证与原文的更新同步,该精简的我也了精简一下,如有需要我可以把原文贴出来。

         要知道Godaddy的免费空间禁用了mail()函数,无法通过调用mail()发送邮件,因此一些有关功能就无法使用,比如wp-db-backup插件的定期备份数据库并发送至指定邮箱的功能。下面这种方法可以解决该问题。

  1. 下载插件WP Mail SMTP,这个直接在后台就可以安装。
  2. 建立一个Godaddy的E-mail帐号,这个似乎只要你买了域名或者空间就可以免费获得,不过要自己去开通一下。如果你已经开通并有一个帐号了就不必再申请了。
  3. 进入后台“设置”–“EMail”,按如下方法填写:
  • From Email: 按这种格式填写“[email protected]",注意必须是刚才注册那个Godaddy的email帐号
  • From Name:应该随便填,就是现实是谁发的邮件,留空则发件人是wordpress
  • Mailer:选择“Send all WordPress emails via SMTP”
  • SMTP Host:填写“relay-hosting.secureserver.net”
  • SMTP Port:填写“25”(这是默认SMTP端口)
  • Encryption:选择“No encryption“
  • Authentication:选择”No: Do not use SMTP authentication“,下面那个用户名密码不用填

之后的工作:

  1. 点Update Options按钮保存
  2. 在最下面那个框填写一个你的email,点Send Test按钮测试一下

如果成功了将出现以下信息(点击右边那个按钮展开)

Test Message Sent
 
The result was:
 
bool(true)
The SMTP debugging output is shown below:
 
SMTP -&gt; FROM SERVER:
220 p3nlsmtp01.shr.prod.phx3.secureserver.net ESMTP
SMTP -&gt; FROM SERVER:
250-p3nlsmtp01.shr.prod.phx3.secureserver.net
250-PIPELINING
250-SIZE 31457280
250 8BITMIME
SMTP -&gt; FROM SERVER:
250 ok
SMTP -&gt; FROM SERVER:
250 ok
SMTP -&gt; FROM SERVER:
354 go ahead punk, make my day
SMTP -&gt; FROM SERVER:
250 ok 1292396492 qp 31062 by p3nlsmtp01.shr.prod.phx3.secureserver.net
SMTP -&gt; FROM SERVER:
221 p3nlsmtp01.shr.prod.phx3.secureserver.net Goodbye.

眼花缭乱是吗?其实直接看第5行那个bool(true)就行,如果是true就表示成功了

LongTimeNoC 主题确定

经过几番周折,本博客终于确定了主题–Suffusion,先后测试了Mystique、Twenty Ten、LightWord、Atahualpa等多款主题,发现不是功能不好,定制性不强,中文支持不完善,就是太大众化,随便找几个wordpress的博客,能有一裤兜字用一个主题的,样式还挺统一的(如果可定制性强就不怕变得太大众化了)。这些主题中Atahualpa给我留下很深刻的印象,可定制性太强了,后台设置可以用眼花缭乱来形容,最终还是因RSS Feed中文乱码问题而放弃了。Suffusion,也就是现在这个主题最开始我安装过一遍,看到后台主题那部分没有设置选项就直接换成别的主题了,今天又安装了一次突然发现该主题的后台设置原来在后台的最底部,其可定制程度虽不能和Atahualpa相比,但也确实够丰富了,具体还是由读者来发现吧。主题下载直接在wordpress的主题安装中搜索即可,后台设置是英文版的,有网友对其做了汉化(本文发布时是3.7.1版,和最新版同步),汉化得很不错,下载页面

Suffusion作者博客:http://www.aquoid.com/news/