对于视频播放器,市面上也有很多,也做得比较好,写这篇文章目的只是为了让大家了解播放器的开发过程,也让自己再次熟悉下这部分的代码。有些播放器用SDL进行视频渲染,但是这篇文章只写基础。用vitamio是为了支持多种视频格式如swf、flv、avi、asf、f4v、mkv、mov、rmvb、tp、ts、wmv、m3u8等市面上的视频格式都涵盖了
接下来就是代码开始了。
对于播放器首先要有播放视图,这里抽象为VideoView继承SurfaceView,所有的播放逻辑都这这里。其次有控制视频播放的接口如暂停、播放、进度条,滑动设置视频音量大小等,这里抽象为MediaController继承FrameLayout。
VideoView代码如下:
package com.example.videotest.widget; import android.app.Activity; import android.app.AlertDialog; import android.content.Context; import android.content.DialogInterface; import android.content.Intent; import android.media.AudioManager; import android.net.Uri; import android.util.AttributeSet; import android.util.DisplayMetrics; import android.view.KeyEvent; import android.view.MotionEvent; import android.view.SurfaceHolder; import android.view.SurfaceView; import android.view.View; import android.view.ViewGroup.LayoutParams; import com.example.videotest.R; import com.example.videotest.widget.MediaController.MediaPlayerControl; import java.io.IOException; import java.util.HashMap; import java.util.List; import io.vov.utils.Log; import io.vov.vitamio.MediaPlayer; import io.vov.vitamio.MediaPlayer.OnBufferingUpdateListener; import io.vov.vitamio.MediaPlayer.OnCompletionListener; import io.vov.vitamio.MediaPlayer.OnErrorListener; import io.vov.vitamio.MediaPlayer.OnInfoListener; import io.vov.vitamio.MediaPlayer.OnPreparedListener; import io.vov.vitamio.MediaPlayer.OnSeekCompleteListener; import io.vov.vitamio.MediaPlayer.OnSubtitleUpdateListener; import io.vov.vitamio.MediaPlayer.OnVideoSizeChangedListener; public class VideoView extends SurfaceView implements MediaPlayerControl{ private Uri mUri; private long mDuration; /** * 视频状态机 */ private static final int STATE_ERROR = -1; private static final int STATE_IDLE = 0; private static final int STATE_PREPARING = 1; private static final int STATE_PREPARED = 2; private static final int STATE_PLAYING = 3; private static final int STATE_PAUSED = 4; private static final int STATE_PLAYBACK_COMPLETED = 5; private static final int STATE_SUSPEND = 6; private static final int STATE_RESUME = 7; private static final int STATE_SUSPEND_UNSUPPORTED = 8; private int mCurrentState = STATE_IDLE; private int mTargetState = STATE_IDLE; private float mAspectRatio = 0; private int mVideoLayout = VIDEO_LAYOUT_SCALE; public static final int VIDEO_LAYOUT_ORIGIN = 0; public static final int VIDEO_LAYOUT_SCALE = 1; public static final int VIDEO_LAYOUT_STRETCH = 2; public static final int VIDEO_LAYOUT_ZOOM = 3; private SurfaceHolder mSurfaceHolder = null; private MediaPlayer mMediaPlayer = null; private int mVideoWidth; private int mVideoHeight; private float mVideoAspectRatio; private int mSurfaceWidth; private int mSurfaceHeight; private MediaController mMediaController; private OnCompletionListener mOnCompletionListener; private OnPreparedListener mOnPreparedListener; private OnErrorListener mOnErrorListener; private OnSeekCompleteListener mOnSeekCompleteListener; private OnSubtitleUpdateListener mOnSubtitleUpdateListener; private OnInfoListener mOnInfoListener; private OnBufferingUpdateListener mOnBufferingUpdateListener; private int mCurrentBufferPercentage; private long mSeekWhenPrepared; private boolean mCanPause = true; private boolean mCanSeekBack = true; private boolean mCanSeekForward = true; private Context mContext; public VideoView(Context context) { super(context); initVideoView(context); } public VideoView(Context context, AttributeSet attrs) { this(context, attrs, 0); } public VideoView(Context context, AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); initVideoView(context); } @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { int width = getDefaultSize(mVideoWidth, widthMeasureSpec); int height = getDefaultSize(mVideoHeight, heightMeasureSpec); setMeasuredDimension(width, height); } /** * Set the display options * * @param layout <ul> * <li>{@link #VIDEO_LAYOUT_ORIGIN} * <li>{@link #VIDEO_LAYOUT_SCALE} * <li>{@link #VIDEO_LAYOUT_STRETCH} * <li>{@link #VIDEO_LAYOUT_ZOOM} * </ul> * @param aspectRatio 视频宽高比,将自动检测 */ public void setVideoLayout(int layout, float aspectRatio) { LayoutParams lp = getLayoutParams(); DisplayMetrics disp = mContext.getResources().getDisplayMetrics(); int windowWidth = disp.widthPixels, windowHeight = disp.heightPixels; float windowRatio = windowWidth / (float) windowHeight; float videoRatio = aspectRatio <= 0.01f ? mVideoAspectRatio : aspectRatio; mSurfaceHeight = mVideoHeight; mSurfaceWidth = mVideoWidth; if (VIDEO_LAYOUT_ORIGIN == layout && mSurfaceWidth < windowWidth && mSurfaceHeight < windowHeight) { lp.width = (int) (mSurfaceHeight * videoRatio); lp.height = mSurfaceHeight; } else if (layout == VIDEO_LAYOUT_ZOOM) { lp.width = windowRatio > videoRatio ? windowWidth : (int) (videoRatio * windowHeight); lp.height = windowRatio < videoRatio ? windowHeight : (int) (windowWidth / videoRatio); } else { // boolean full = layout == VIDEO_LAYOUT_STRETCH; // lp.width = (full || windowRatio < videoRatio) ? windowWidth : (int) (videoRatio * windowHeight); // lp.height = (full || windowRatio > videoRatio) ? windowHeight : (int) (windowWidth / videoRatio); } setLayoutParams(lp); getHolder().setFixedSize(mSurfaceWidth, mSurfaceHeight); Log.d("VIDEO: %dx%dx%f, Surface: %dx%d, LP: %dx%d, Window: %dx%dx%f", mVideoWidth, mVideoHeight, mVideoAspectRatio, mSurfaceWidth, mSurfaceHeight, lp.width, lp.height, windowWidth, windowHeight, windowRatio); mVideoLayout = layout; mAspectRatio = aspectRatio; } private void initVideoView(Context ctx) { mContext = ctx; mVideoWidth = 0; mVideoHeight = 0; getHolder().addCallback(mSHCallback); setFocusable(true); setFocusableInTouchMode(true); requestFocus(); mCurrentState = STATE_IDLE; mTargetState = STATE_IDLE; if (ctx instanceof Activity) ((Activity) ctx).setVolumeControlStream(AudioManager.STREAM_MUSIC); } public boolean isValid() { return (mSurfaceHolder != null && mSurfaceHolder.getSurface().isValid()); } public void setVideoPath(String path) { setVideoURI(Uri.parse(path)); } public void setVideoURI(Uri uri) { mUri = uri; mSeekWhenPrepared = 0; openVideo(); requestLayout(); invalidate(); } public void stopPlayback() { if (mMediaPlayer != null) { mMediaPlayer.stop(); mMediaPlayer.release(); mMediaPlayer = null; mCurrentState = STATE_IDLE; mTargetState = STATE_IDLE; } } private void openVideo() { if (mUri == null || mSurfaceHolder == null) return; Intent i = new Intent("com.android.music.musicservicecommand"); i.putExtra("command", "pause"); mContext.sendBroadcast(i); release(false); try { mDuration = -1; mCurrentBufferPercentage = 0; mMediaPlayer = new MediaPlayer(mContext); mMediaPlayer.setOnPreparedListener(mPreparedListener); mMediaPlayer.setOnVideoSizeChangedListener(mSizeChangedListener); mMediaPlayer.setOnCompletionListener(mCompletionListener); mMediaPlayer.setOnErrorListener(mErrorListener); mMediaPlayer.setOnBufferingUpdateListener(mBufferingUpdateListener); mMediaPlayer.setOnInfoListener(mInfoListener); mMediaPlayer.setOnSeekCompleteListener(mSeekCompleteListener); mMediaPlayer.setOnSubtitleUpdateListener(mSubtitleUpdateListener); mMediaPlayer.setDataSource(mContext, mUri); mMediaPlayer.setDisplay(mSurfaceHolder); mMediaPlayer.setScreenOnWhilePlaying(true); mMediaPlayer.prepareAsync(); mCurrentState = STATE_PREPARING; attachMediaController(); } catch (IOException ex) { Log.e("Unable to open content: " + mUri, ex); mCurrentState = STATE_ERROR; mTargetState = STATE_ERROR; mErrorListener.onError(mMediaPlayer, MediaPlayer.MEDIA_ERROR_UNKNOWN, 0); return; } catch (IllegalArgumentException ex) { Log.e("Unable to open content: " + mUri, ex); mCurrentState = STATE_ERROR; mTargetState = STATE_ERROR; mErrorListener.onError(mMediaPlayer, MediaPlayer.MEDIA_ERROR_UNKNOWN, 0); return; } } public void setMediaController(MediaController controller) { if (mMediaController != null) mMediaController.hide(); mMediaController = controller; attachMediaController(); } // View mView; // public void setView(View view){ // this.mView = view; // } private void attachMediaController() { if (mMediaPlayer != null && mMediaController != null) { mMediaController.setMediaPlayer(this); //View anchorView = ((LayoutInflater) mContext.getSystemService(Context.LAYOUT_INFLATER_SERVICE)).inflate(R.layout.vedio_play_activity, null).findViewById(R.id.media_player_layout); View anchorView = this.getParent() instanceof View ? (View) this.getParent() : this; mMediaController.setAnchorView(anchorView); mMediaController.setEnabled(isInPlaybackState()); if (mUri != null) { List<String> paths = mUri.getPathSegments(); String name = paths == null || paths.isEmpty() ? "null" : paths.get(paths.size() - 1); mMediaController.setFileName(name); } } } OnVideoSizeChangedListener mSizeChangedListener = new OnVideoSizeChangedListener() { @Override public void onVideoSizeChanged(MediaPlayer mp, int width, int height) { Log.d("onVideoSizeChanged: (%dx%d)", width, height); mVideoWidth = mp.getVideoWidth(); mVideoHeight = mp.getVideoHeight(); mVideoAspectRatio = mp.getVideoAspectRatio(); if (mVideoWidth != 0 && mVideoHeight != 0) setVideoLayout(mVideoLayout, mAspectRatio); } }; OnPreparedListener mPreparedListener = new OnPreparedListener() { @Override public void onPrepared(MediaPlayer mp) { Log.d("onPrepared"); mCurrentState = STATE_PREPARED; mTargetState = STATE_PLAYING; if (mOnPreparedListener != null) mOnPreparedListener.onPrepared(mMediaPlayer); if (mMediaController != null) mMediaController.setEnabled(true); mVideoWidth = mp.getVideoWidth(); mVideoHeight = mp.getVideoHeight(); mVideoAspectRatio = mp.getVideoAspectRatio(); long seekToPosition = mSeekWhenPrepared; if (seekToPosition != 0) seekTo(seekToPosition); if (mVideoWidth != 0 && mVideoHeight != 0) { setVideoLayout(mVideoLayout, mAspectRatio); if (mSurfaceWidth == mVideoWidth && mSurfaceHeight == mVideoHeight) { if (mTargetState == STATE_PLAYING) { start(); if (mMediaController != null) mMediaController.show(); } else if (!isPlaying() && (seekToPosition != 0 || getCurrentPosition() > 0)) { if (mMediaController != null) mMediaController.show(0); } } } else if (mTargetState == STATE_PLAYING) { start(); } } }; private OnCompletionListener mCompletionListener = new OnCompletionListener() { @Override public void onCompletion(MediaPlayer mp) { Log.d("onCompletion"); mCurrentState = STATE_PLAYBACK_COMPLETED; mTargetState = STATE_PLAYBACK_COMPLETED; if (mMediaController != null) mMediaController.hide(); if (mOnCompletionListener != null) mOnCompletionListener.onCompletion(mMediaPlayer); } }; private OnErrorListener mErrorListener = new OnErrorListener() { @Override public boolean onError(MediaPlayer mp, int framework_err, int impl_err) { Log.d("Error: %d, %d", framework_err, impl_err); mCurrentState = STATE_ERROR; mTargetState = STATE_ERROR; if (mMediaController != null) mMediaController.hide(); if (mOnErrorListener != null) { if (mOnErrorListener.onError(mMediaPlayer, framework_err, impl_err)) return true; } if (getWindowToken() != null) { int message = framework_err == MediaPlayer.MEDIA_ERROR_NOT_VALID_FOR_PROGRESSIVE_PLAYBACK ? R.string.VideoView_error_text_invalid_progressive_playback : R.string.VideoView_error_text_unknown; new AlertDialog.Builder(mContext).setTitle(R.string.VideoView_error_title).setMessage(message).setPositiveButton(R.string.VideoView_error_button, new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int whichButton) { if (mOnCompletionListener != null) mOnCompletionListener.onCompletion(mMediaPlayer); } }).setCancelable(false).show(); } return true; } }; private OnBufferingUpdateListener mBufferingUpdateListener = new OnBufferingUpdateListener() { @Override public void onBufferingUpdate(MediaPlayer mp, int percent) { mCurrentBufferPercentage = percent; if (mOnBufferingUpdateListener != null) mOnBufferingUpdateListener.onBufferingUpdate(mp, percent); } }; private OnInfoListener mInfoListener = new OnInfoListener() { @Override public boolean onInfo(MediaPlayer mp, int what, int extra) { Log.d("onInfo: (%d, %d)", what, extra); if (mOnInfoListener != null) { mOnInfoListener.onInfo(mp, what, extra); } else if (mMediaPlayer != null) { if (what == MediaPlayer.MEDIA_INFO_BUFFERING_START) mMediaPlayer.pause(); else if (what == MediaPlayer.MEDIA_INFO_BUFFERING_END) mMediaPlayer.start(); } return true; } }; private OnSeekCompleteListener mSeekCompleteListener = new OnSeekCompleteListener() { @Override public void onSeekComplete(MediaPlayer mp) { Log.d("onSeekComplete"); if (mOnSeekCompleteListener != null) mOnSeekCompleteListener.onSeekComplete(mp); } }; private OnSubtitleUpdateListener mSubtitleUpdateListener = new OnSubtitleUpdateListener() { @Override public void onSubtitleUpdate(byte[] pixels, int width, int height) { Log.i("onSubtitleUpdate: bitmap subtitle, %dx%d", width, height); if (mOnSubtitleUpdateListener != null) mOnSubtitleUpdateListener.onSubtitleUpdate(pixels, width, height); } @Override public void onSubtitleUpdate(String text) { Log.i("onSubtitleUpdate: %s", text); if (mOnSubtitleUpdateListener != null) mOnSubtitleUpdateListener.onSubtitleUpdate(text); } }; public void setOnPreparedListener(OnPreparedListener l) { mOnPreparedListener = l; } public void setOnCompletionListener(OnCompletionListener l) { mOnCompletionListener = l; } public void setOnErrorListener(OnErrorListener l) { mOnErrorListener = l; } public void setOnBufferingUpdateListener(OnBufferingUpdateListener l) { mOnBufferingUpdateListener = l; } public void setOnSeekCompleteListener(OnSeekCompleteListener l) { mOnSeekCompleteListener = l; } public void setOnSubtitleUpdateListener(OnSubtitleUpdateListener l) { mOnSubtitleUpdateListener = l; } public void setOnInfoListener(OnInfoListener l) { mOnInfoListener = l; } SurfaceHolder.Callback mSHCallback = new SurfaceHolder.Callback() { @Override public void surfaceChanged(SurfaceHolder holder, int format, int w, int h) { mSurfaceWidth = w; mSurfaceHeight = h; boolean isValidState = (mTargetState == STATE_PLAYING); boolean hasValidSize = (mVideoWidth == w && mVideoHeight == h); if (mMediaPlayer != null && isValidState && hasValidSize) { if (mSeekWhenPrepared != 0) seekTo(mSeekWhenPrepared); start(); if (mMediaController != null) { if (mMediaController.isShowing()) mMediaController.hide(); mMediaController.show(); } } } @Override public void surfaceCreated(SurfaceHolder holder) { mSurfaceHolder = holder; if (mMediaPlayer != null && mCurrentState == STATE_SUSPEND && mTargetState == STATE_RESUME) { mMediaPlayer.setDisplay(mSurfaceHolder); resume(); } else { openVideo(); } } @Override public void surfaceDestroyed(SurfaceHolder holder) { mSurfaceHolder = null; if (mMediaController != null) mMediaController.hide(); if (mCurrentState != STATE_SUSPEND) release(true); } }; private void release(boolean cleartargetstate) { if (mMediaPlayer != null) { mMediaPlayer.reset(); mMediaPlayer.release(); mMediaPlayer = null; mCurrentState = STATE_IDLE; if (cleartargetstate) mTargetState = STATE_IDLE; } } @Override public boolean onTouchEvent(MotionEvent ev) { if (isInPlaybackState() && mMediaController != null) toggleMediaControlsVisiblity(); return false; } @Override public boolean onTrackballEvent(MotionEvent ev) { if (isInPlaybackState() && mMediaController != null) toggleMediaControlsVisiblity(); return false; } @Override public boolean onKeyDown(int keyCode, KeyEvent event) { boolean isKeyCodeSupported = keyCode != KeyEvent.KEYCODE_BACK && keyCode != KeyEvent.KEYCODE_VOLUME_UP && keyCode != KeyEvent.KEYCODE_VOLUME_DOWN && keyCode != KeyEvent.KEYCODE_MENU && keyCode != KeyEvent.KEYCODE_CALL && keyCode != KeyEvent.KEYCODE_ENDCALL; if (isInPlaybackState() && isKeyCodeSupported && mMediaController != null) { if (keyCode == KeyEvent.KEYCODE_HEADSETHOOK || keyCode == KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE || keyCode == KeyEvent.KEYCODE_SPACE) { if (mMediaPlayer.isPlaying()) { pause(); mMediaController.show(); } else { start(); mMediaController.hide(); } return true; } else if (keyCode == KeyEvent.KEYCODE_MEDIA_STOP && mMediaPlayer.isPlaying()) { pause(); mMediaController.show(); } else { toggleMediaControlsVisiblity(); } } return super.onKeyDown(keyCode, event); } private void toggleMediaControlsVisiblity() { if (mMediaController.isShowing()) { mMediaController.hide(); } else { mMediaController.show(); } } @Override public void start() { if (isInPlaybackState()) { mMediaPlayer.start(); mCurrentState = STATE_PLAYING; } mTargetState = STATE_PLAYING; } @Override public void pause() { if (isInPlaybackState()) { if (mMediaPlayer.isPlaying()) { mMediaPlayer.pause(); mCurrentState = STATE_PAUSED; } } mTargetState = STATE_PAUSED; } public void suspend() { if (isInPlaybackState()) { release(false); mCurrentState = STATE_SUSPEND_UNSUPPORTED; Log.d("Unable to suspend video. Release MediaPlayer."); } } public void resume() { if (mSurfaceHolder == null && mCurrentState == STATE_SUSPEND) { mTargetState = STATE_RESUME; } else if (mCurrentState == STATE_SUSPEND_UNSUPPORTED) { openVideo(); } } @Override public long getDuration() { if (isInPlaybackState()) { if (mDuration > 0) return mDuration; mDuration = mMediaPlayer.getDuration(); return mDuration; } mDuration = -1; return mDuration; } @Override public long getCurrentPosition() { if (isInPlaybackState()) return mMediaPlayer.getCurrentPosition(); return 0; } @Override public void seekTo(long msec) { if (isInPlaybackState()) { mMediaPlayer.seekTo(msec); mSeekWhenPrepared = 0; } else { mSeekWhenPrepared = msec; } } @Override public boolean isPlaying() { return isInPlaybackState() && mMediaPlayer.isPlaying(); } @Override public int getBufferPercentage() { if (mMediaPlayer != null) return mCurrentBufferPercentage; return 0; } public void setVolume(float leftVolume, float rightVolume) { if (mMediaPlayer != null) mMediaPlayer.setVolume(leftVolume, rightVolume); } public int getVideoWidth() { return mVideoWidth; } public int getVideoHeight() { return mVideoHeight; } public float getVideoAspectRatio() { return mVideoAspectRatio; } public void setVideoQuality(int quality) { if (mMediaPlayer != null) mMediaPlayer.setVideoQuality(quality); } public void setBufferSize(int bufSize) { if (mMediaPlayer != null) mMediaPlayer.setBufferSize(bufSize); } public boolean isBuffering() { if (mMediaPlayer != null) return mMediaPlayer.isBuffering(); return false; } public void setMetaEncoding(String encoding) { if (mMediaPlayer != null) mMediaPlayer.setMetaEncoding(encoding); } public String getMetaEncoding() { if (mMediaPlayer != null) return mMediaPlayer.getMetaEncoding(); return null; } public HashMap<String, Integer> getAudioTrackMap(String encoding) { if (mMediaPlayer != null) return mMediaPlayer.getAudioTrackMap(encoding); return null; } public int getAudioTrack() { if (mMediaPlayer != null) return mMediaPlayer.getAudioTrack(); return -1; } public void setAudioTrack(int audioIndex) { if (mMediaPlayer != null) mMediaPlayer.setAudioTrack(audioIndex); } public void setSubShown(boolean shown) { if (mMediaPlayer != null) mMediaPlayer.setSubShown(shown); } public void setSubEncoding(String encoding) { if (mMediaPlayer != null) mMediaPlayer.setSubEncoding(encoding); } public int getSubLocation() { if (mMediaPlayer != null) return mMediaPlayer.getSubLocation(); return -1; } public void setSubPath(String subPath) { if (mMediaPlayer != null) mMediaPlayer.setSubPath(subPath); } public String getSubPath() { if (mMediaPlayer != null) return mMediaPlayer.getSubPath(); return null; } public void setSubTrack(int trackId) { if (mMediaPlayer != null) mMediaPlayer.setSubTrack(trackId); } public int getSubTrack() { if (mMediaPlayer != null) return mMediaPlayer.getSubTrack(); return -1; } public HashMap<String, Integer> getSubTrackMap(String encoding) { if (mMediaPlayer != null) return mMediaPlayer.getSubTrackMap(encoding); return null; } protected boolean isInPlaybackState() { return (mMediaPlayer != null && mCurrentState != STATE_ERROR && mCurrentState != STATE_IDLE && mCurrentState != STATE_PREPARING); } @Override public boolean canPause() { return mCanPause; } @Override public boolean canSeekBackward() { return mCanSeekBack; } @Override public boolean canSeekForward() { return mCanSeekForward; } }代码都比较好理解。
接下来是控制器
package com.example.videotest.widget; import com.example.videotest.R; import io.vov.utils.Log; import io.vov.utils.StringUtils; import android.content.Context; import android.media.AudioManager; import android.os.Handler; import android.os.Message; import android.util.AttributeSet; import android.view.Gravity; import android.view.KeyEvent; import android.view.LayoutInflater; import android.view.MotionEvent; import android.view.View; import android.widget.FrameLayout; import android.widget.ImageButton; import android.widget.PopupWindow; import android.widget.ProgressBar; import android.widget.SeekBar; import android.widget.TextView; import android.widget.SeekBar.OnSeekBarChangeListener; public class MediaController extends FrameLayout { private MediaPlayerControl mPlayer; private Context mContext; private PopupWindow mWindow; private int mAnimStyle; private View mAnchor; private View mRoot; private ProgressBar mProgress; private TextView mEndTime, mCurrentTime; private TextView mFileName; private OutlineTextView mInfoView; private String mTitle; private long mDuration; private boolean mShowing; private boolean mDragging; private boolean mInstantSeeking = true; private static final int sDefaultTimeout = 3000; private static final int FADE_OUT = 1; private static final int SHOW_PROGRESS = 2; private boolean mFromXml = false; private ImageButton mPauseButton; private AudioManager mAM; public MediaController(Context context, AttributeSet attrs) { super(context, attrs); mRoot = this; mFromXml = true; initController(context); } public MediaController(Context context) { super(context); if (!mFromXml && initController(context)) initFloatingWindow(); } private boolean initController(Context context) { mContext = context; mAM = (AudioManager) mContext.getSystemService(Context.AUDIO_SERVICE); return true; } @Override public void onFinishInflate() { if (mRoot != null) initControllerView(mRoot); } private void initFloatingWindow() { mWindow = new PopupWindow(mContext); mWindow.setFocusable(false); mWindow.setBackgroundDrawable(null); mWindow.setOutsideTouchable(true); mAnimStyle = android.R.style.Animation; } /** * Set the view that acts as the anchor for the control view. This can for * example be a VideoView, or your Activity's main view. * * @param view The view to which to anchor the controller when it is visible. */ public void setAnchorView(View view) { mAnchor = view; if (!mFromXml) { removeAllViews(); mRoot = makeControllerView(); mWindow.setContentView(mRoot); mWindow.setWidth(android.view.ViewGroup.LayoutParams.MATCH_PARENT); mWindow.setHeight(android.view.ViewGroup.LayoutParams.WRAP_CONTENT); } initControllerView(mRoot); } /** * * @return The controller view. */ protected View makeControllerView() { return ((LayoutInflater) mContext.getSystemService(Context.LAYOUT_INFLATER_SERVICE)).inflate(R.layout.mediacontroller, this); } private void initControllerView(View v) { mPauseButton = (ImageButton) v.findViewById(R.id.mediacontroller_play_pause); if (mPauseButton != null) { mPauseButton.requestFocus(); mPauseButton.setOnClickListener(mPauseListener); } mProgress = (ProgressBar) v.findViewById(R.id.mediacontroller_seekbar); if (mProgress != null) { if (mProgress instanceof SeekBar) { SeekBar seeker = (SeekBar) mProgress; seeker.setOnSeekBarChangeListener(mSeekListener); seeker.setThumbOffset(1); } mProgress.setMax(1000); } mEndTime = (TextView) v.findViewById(R.id.mediacontroller_time_total); mCurrentTime = (TextView) v.findViewById(R.id.mediacontroller_time_current); mFileName = (TextView) v.findViewById(R.id.mediacontroller_file_name); if (mFileName != null) mFileName.setText(mTitle); } public void setMediaPlayer(MediaPlayerControl player) { mPlayer = player; updatePausePlay(); } /** * * @param seekWhenDragging */ public void setInstantSeeking(boolean seekWhenDragging) { mInstantSeeking = seekWhenDragging; } public void show() { show(sDefaultTimeout); } /** * * @param name */ public void setFileName(String name) { mTitle = name; if (mFileName != null) mFileName.setText(mTitle); } /** * MediaController * * @param v */ public void setInfoView(OutlineTextView v) { mInfoView = v; } private void disableUnsupportedButtons() { try { if (mPauseButton != null && !mPlayer.canPause()) mPauseButton.setEnabled(false); } catch (IncompatibleClassChangeError ex) { } } /** * @param animationStyle 控制器出现时使用动画风格 * 和消失。设置为-1为默认的动画,0表示没有动画,或 * 一个显式动画资源标识符。 */ public void setAnimationStyle(int animationStyle) { mAnimStyle = animationStyle; } /** * 在屏幕上显示控制器。它会显示timeout长时间后自动消失 * * @param timeout */ public void show(int timeout) { if (!mShowing && mAnchor != null && mAnchor.getWindowToken() != null) { if (mPauseButton != null) mPauseButton.requestFocus(); disableUnsupportedButtons(); if (mFromXml) { setVisibility(View.VISIBLE); } else { // int[] location = new int[2]; // // mAnchor.getLocationOnScreen(location); // Rect anchorRect = new Rect(location[0], location[1], location[0] + mAnchor.getWidth(), location[1] + mAnchor.getHeight()); mWindow.setAnimationStyle(mAnimStyle); mWindow.showAtLocation(mAnchor, Gravity.NO_GRAVITY, mAnchor.getWidth(), mAnchor.getHeight()); } mShowing = true; if (mShownListener != null) mShownListener.onShown(); } updatePausePlay(); mHandler.sendEmptyMessage(SHOW_PROGRESS); if (timeout != 0) { mHandler.removeMessages(FADE_OUT); mHandler.sendMessageDelayed(mHandler.obtainMessage(FADE_OUT), timeout); } } public boolean isShowing() { return mShowing; } /** * 隐藏控制条 */ public void hide() { if (mAnchor == null) return; if (mShowing) { try { mHandler.removeMessages(SHOW_PROGRESS); if (mFromXml) setVisibility(View.GONE); else mWindow.dismiss(); } catch (IllegalArgumentException ex) { Log.d("MediaController already removed"); } mShowing = false; if (mHiddenListener != null) mHiddenListener.onHidden(); } } public interface OnShownListener { public void onShown(); } private OnShownListener mShownListener; public void setOnShownListener(OnShownListener l) { mShownListener = l; } public interface OnHiddenListener { public void onHidden(); } private OnHiddenListener mHiddenListener; public void setOnHiddenListener(OnHiddenListener l) { mHiddenListener = l; } private Handler mHandler = new Handler() { @Override public void handleMessage(Message msg) { long pos; switch (msg.what) { case FADE_OUT: //hide(); break; case SHOW_PROGRESS: pos = setProgress(); if (!mDragging && mShowing) { msg = obtainMessage(SHOW_PROGRESS); sendMessageDelayed(msg, 1000 - (pos % 1000)); updatePausePlay(); } break; } } }; private long setProgress() { if (mPlayer == null || mDragging) return 0; long position = mPlayer.getCurrentPosition(); long duration = mPlayer.getDuration(); if (mProgress != null) { if (duration > 0) { long pos = 1000L * position / duration; mProgress.setProgress((int) pos); } int percent = mPlayer.getBufferPercentage(); mProgress.setSecondaryProgress(percent * 10); } mDuration = duration; if (mEndTime != null) mEndTime.setText(StringUtils.generateTime(mDuration)); if (mCurrentTime != null) mCurrentTime.setText(StringUtils.generateTime(position)); return position; } @Override public boolean onTouchEvent(MotionEvent event) { show(sDefaultTimeout); return true; } @Override public boolean onTrackballEvent(MotionEvent ev) { show(sDefaultTimeout); return false; } /** * 接收事件 * * @param event * @return */ @Override public boolean dispatchKeyEvent(KeyEvent event) { int keyCode = event.getKeyCode(); if (event.getRepeatCount() == 0 && (keyCode == KeyEvent.KEYCODE_HEADSETHOOK || keyCode == KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE || keyCode == KeyEvent.KEYCODE_SPACE)) { doPauseResume(); show(sDefaultTimeout); if (mPauseButton != null) mPauseButton.requestFocus(); return true; } else if (keyCode == KeyEvent.KEYCODE_MEDIA_STOP) { if (mPlayer.isPlaying()) { mPlayer.pause(); updatePausePlay(); } return true; } else if (keyCode == KeyEvent.KEYCODE_BACK || keyCode == KeyEvent.KEYCODE_MENU) { hide(); return true; } else { show(sDefaultTimeout); } return super.dispatchKeyEvent(event); } private View.OnClickListener mPauseListener = new View.OnClickListener() { @Override public void onClick(View v) { doPauseResume(); show(sDefaultTimeout); } }; private void updatePausePlay() { if (mRoot == null || mPauseButton == null) return; if (mPlayer.isPlaying()) mPauseButton.setImageResource(R.drawable.mediacontroller_pause_button); else mPauseButton.setImageResource(R.drawable.mediacontroller_play_button); } private void doPauseResume() { if (mPlayer.isPlaying()) mPlayer.pause(); else mPlayer.start(); updatePausePlay(); } private OnSeekBarChangeListener mSeekListener = new OnSeekBarChangeListener() { @Override public void onStartTrackingTouch(SeekBar bar) { mDragging = true; show(3600000); mHandler.removeMessages(SHOW_PROGRESS); if (mInstantSeeking) mAM.setStreamMute(AudioManager.STREAM_MUSIC, true); if (mInfoView != null) { mInfoView.setText(""); mInfoView.setVisibility(View.VISIBLE); } } /** * 进度条改变回调主要设置进度和时间 * @param bar * @param progress * @param fromuser */ @Override public void onProgressChanged(SeekBar bar, int progress, boolean fromuser) { if (!fromuser) return; long newposition = (mDuration * progress) / 1000; String time = StringUtils.generateTime(newposition); if (mInstantSeeking) mPlayer.seekTo(newposition); if (mInfoView != null) mInfoView.setText(time); if (mCurrentTime != null) mCurrentTime.setText(time); } @Override public void onStopTrackingTouch(SeekBar bar) { if (!mInstantSeeking) mPlayer.seekTo((mDuration * bar.getProgress()) / 1000); if (mInfoView != null) { mInfoView.setText(""); mInfoView.setVisibility(View.GONE); } show(sDefaultTimeout); mHandler.removeMessages(SHOW_PROGRESS); mAM.setStreamMute(AudioManager.STREAM_MUSIC, false); mDragging = false; mHandler.sendEmptyMessageDelayed(SHOW_PROGRESS, 1000); } }; @Override public void setEnabled(boolean enabled) { if (mPauseButton != null) mPauseButton.setEnabled(enabled); if (mProgress != null) mProgress.setEnabled(enabled); disableUnsupportedButtons(); super.setEnabled(enabled); } /** * 控制接口 */ public interface MediaPlayerControl { void start(); void pause(); long getDuration(); long getCurrentPosition(); void seekTo(long pos); boolean isPlaying(); int getBufferPercentage(); boolean canPause(); boolean canSeekBackward(); boolean canSeekForward(); } }还有一个自定义TextView是用来显示视频信息的。
/* * Copyright (C) 2011 Cedric Fung (wolfplanet@gmail.com) * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.example.videotest.widget; import android.content.Context; import android.graphics.Canvas; import android.graphics.Paint; import android.graphics.Typeface; import android.text.Layout; import android.text.StaticLayout; import android.text.TextPaint; import android.util.AttributeSet; import android.widget.TextView; public class OutlineTextView extends TextView { public OutlineTextView(Context context) { super(context); initPaint(); } public OutlineTextView(Context context, AttributeSet attrs) { super(context, attrs); initPaint(); } public OutlineTextView(Context context, AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); initPaint(); } private void initPaint() { mTextPaint = new TextPaint(); mTextPaint.setAntiAlias(true); mTextPaint.setTextSize(getTextSize()); mTextPaint.setColor(mColor); mTextPaint.setStyle(Paint.Style.FILL); mTextPaint.setTypeface(getTypeface()); mTextPaintOutline = new TextPaint(); mTextPaintOutline.setAntiAlias(true); mTextPaintOutline.setTextSize(getTextSize()); mTextPaintOutline.setColor(mBorderColor); mTextPaintOutline.setStyle(Paint.Style.STROKE); mTextPaintOutline.setTypeface(getTypeface()); mTextPaintOutline.setStrokeWidth(mBorderSize); } public void setText(String text) { super.setText(text); mText = text.toString(); requestLayout(); invalidate(); } @Override public void setTextSize(float size) { super.setTextSize(size); requestLayout(); invalidate(); initPaint(); } @Override public void setTextColor(int color) { super.setTextColor(color); mColor = color; invalidate(); initPaint(); } @Override public void setShadowLayer(float radius, float dx, float dy, int color) { super.setShadowLayer(radius, dx, dy, color); mBorderSize = radius; mBorderColor = color; requestLayout(); invalidate(); initPaint(); } @Override public void setTypeface(Typeface tf, int style) { super.setTypeface(tf, style); requestLayout(); invalidate(); initPaint(); } @Override public void setTypeface(Typeface tf) { super.setTypeface(tf); requestLayout(); invalidate(); initPaint(); } @Override protected void onDraw(Canvas canvas) { Layout layout = new StaticLayout(getText(), mTextPaintOutline, getWidth(), Layout.Alignment.ALIGN_CENTER, mSpacingMult, mSpacingAdd, mIncludePad); layout.draw(canvas); layout = new StaticLayout(getText(), mTextPaint, getWidth(), Layout.Alignment.ALIGN_CENTER, mSpacingMult, mSpacingAdd, mIncludePad); layout.draw(canvas); } @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { Layout layout = new StaticLayout(getText(), mTextPaintOutline, measureWidth(widthMeasureSpec), Layout.Alignment.ALIGN_CENTER, mSpacingMult, mSpacingAdd, mIncludePad); int ex = (int) (mBorderSize * 2 + 1); setMeasuredDimension(measureWidth(widthMeasureSpec) + ex, measureHeight(heightMeasureSpec) * layout.getLineCount() + ex); } private int measureWidth(int measureSpec) { int result = 0; int specMode = MeasureSpec.getMode(measureSpec); int specSize = MeasureSpec.getSize(measureSpec); if (specMode == MeasureSpec.EXACTLY) { result = specSize; } else { result = (int) mTextPaintOutline.measureText(mText) + getPaddingLeft() + getPaddingRight(); if (specMode == MeasureSpec.AT_MOST) { result = Math.min(result, specSize); } } return result; } private int measureHeight(int measureSpec) { int result = 0; int specMode = MeasureSpec.getMode(measureSpec); int specSize = MeasureSpec.getSize(measureSpec); mAscent = (int) mTextPaintOutline.ascent(); if (specMode == MeasureSpec.EXACTLY) { result = specSize; } else { result = (int) (-mAscent + mTextPaintOutline.descent()) + getPaddingTop() + getPaddingBottom(); if (specMode == MeasureSpec.AT_MOST) { result = Math.min(result, specSize); } } return result; } private TextPaint mTextPaint; private TextPaint mTextPaintOutline; private String mText = ""; private int mAscent = 0; private float mBorderSize; private int mBorderColor; private int mColor; private float mSpacingMult = 1.0f; private float mSpacingAdd = 0; private boolean mIncludePad = true; }由于是用vitamio库来播放视频。因此还得需要一些配置,如下
新建一个包名为
io.vov.vitamio,里面引用资源。
package io.vov.vitamio; public class R { public static final class raw { public static final int libarm = com.example.videotest.R.raw.libarm; public static final int pub = com.example.videotest.R.raw.pub; } }
以下是工具类,因为第一次要初始化。
package com.example.videotest.utils; import android.app.Activity; import android.app.ProgressDialog; import android.content.Intent; import android.os.AsyncTask; import android.os.Bundle; import android.os.Handler; import android.os.Message; import android.view.WindowManager; import io.vov.vitamio.Vitamio; public class InitActivity extends Activity { public static final String FROM_ME = "fromVitamioInitActivity"; public static final String EXTRA_MSG = "EXTRA_MSG"; public static final String EXTRA_FILE = "EXTRA_FILE"; private ProgressDialog mPD; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON); new AsyncTask<Object, Object, Object>() { @Override protected void onPreExecute() { // mPD = new ProgressDialog(InitActivity.this); // mPD.setCancelable(false); // mPD.setMessage(getText(R.string.init_decoders)); // mPD.show(); } @Override protected Object doInBackground(Object... params) { Vitamio.initialize(getApplicationContext()); // if (Vitamio.isInitialized(getApplicationContext())) // return null; // // //反射解压 // try { // Class c = Class.forName("io.vov.vitamio.Vitamio"); // Method extractLibs = c.getDeclaredMethod("extractLibs", new Class[] { android.content.Context.class, int.class }); // extractLibs.setAccessible(true); // extractLibs.invoke(c, new Object[] { getApplicationContext(), R.raw.libarm }); // Field vitamioLibraryPath = c.getDeclaredField("vitamioLibraryPath"); AndroidContextUtils.getDataDir(ctx) + "libs/" // // } catch (NoSuchMethodException e) { // Log.e("extractLibs", e); // e.printStackTrace(); // } catch (IllegalArgumentException e) { // e.printStackTrace(); // } catch (IllegalAccessException e) { // e.printStackTrace(); // } catch (InvocationTargetException e) { // e.printStackTrace(); // } catch (ClassNotFoundException e) { // e.printStackTrace(); // } uiHandler.sendEmptyMessage(0); return null; } }.execute(); } private Handler uiHandler = new Handler() { @Override public void handleMessage(Message msg) { //mPD.dismiss(); Intent src = getIntent(); Intent i = new Intent(); i.setClassName(src.getStringExtra("package"), src.getStringExtra("className")); i.setData(src.getData()); i.putExtras(src); i.putExtra(FROM_ME, true); startActivity(i); finish(); } }; }package com.example.videotest.utils; import android.app.Activity; import android.content.Intent; import io.vov.vitamio.Vitamio; public final class LibsChecker { public static final boolean checkVitamioLibs(Activity ctx, int msgID) { if ((!Vitamio.isInitialized(ctx)) && (!ctx.getIntent().getBooleanExtra("fromVitamioInitActivity", false))) { Intent i = new Intent(); i.setClassName(ctx.getPackageName(), "com.example.videotest.utils.InitActivity");//io.vov.vitamio.activity.InitActivity i.putExtras(ctx.getIntent()); i.setData(ctx.getIntent().getData()); i.putExtra("package", ctx.getPackageName()); i.putExtra("className", ctx.getClass().getName()); i.putExtra("EXTRA_MSG", msgID); ctx.startActivity(i); ctx.finish(); return false; } return true; } }接下来是在Activity使用package com.example.videotest; import android.app.Activity; import android.content.Context; import android.content.Intent; import android.content.pm.ActivityInfo; import android.content.res.Configuration; import android.media.AudioManager; import android.net.Uri; import android.os.Bundle; import android.os.Handler; import android.os.Message; import android.text.TextUtils; import android.util.Log; import android.view.Display; import android.view.GestureDetector; import android.view.GestureDetector.SimpleOnGestureListener; import android.view.MotionEvent; import android.view.View; import android.view.ViewGroup; import android.view.WindowManager; import android.widget.ImageView; import com.example.videotest.utils.LibsChecker; import com.example.videotest.widget.MediaController; import com.example.videotest.widget.VideoView; import io.vov.vitamio.MediaPlayer; import io.vov.vitamio.MediaPlayer.OnCompletionListener; import io.vov.vitamio.MediaPlayer.OnInfoListener; public class VideoPlayActivity extends Activity implements OnCompletionListener, OnInfoListener{ private String mPath; private String mTitle; private VideoView mVideoView; private View mVolumeBrightnessLayout; private ImageView mOperationBg; private ImageView mOperationPercent; private AudioManager mAudioManager; /** 最大声音 */ private int mMaxVolume; /** 当前声音 */ private int mVolume = -1; /** 当前亮度 */ private float mBrightness = -1f; /** 当前缩放模式 */ private int mLayout = VideoView.VIDEO_LAYOUT_ORIGIN; private GestureDetector mGestureDetector; private MediaController mMediaController; private View mLoadingView; private ImageView mDefaultImg; @Override public void onCreate(Bundle bundle) { super.onCreate(bundle); // ~~~ 检测Vitamio是否解压解码包 if (!LibsChecker.checkVitamioLibs(this, R.string.init_decoders)) return; // ~~~ 获取播放地址和标题 Intent intent = getIntent(); if(null == intent){ mPath = "http://player.youku.com/player.php/sid/XMTY4MDg2Nzg4MA==/v.swf"; mTitle = "视频播放"; }else{ mPath = intent.getStringExtra("path"); mTitle = intent.getStringExtra("title"); } if (TextUtils.isEmpty(mPath)) { //mPath = Environment.getExternalStorageDirectory() + "/video/你太猖狂.flv"; mPath = "http://video.52xiyou.com/video/FBSP119/baofeng12503.flv"; } else if (intent.getData() != null) mPath = intent.getData().toString(); // ~~~ 绑定控件 setContentView(R.layout.vedio_play_activity); mVideoView = (VideoView) findViewById(R.id.surface_view); mVolumeBrightnessLayout = findViewById(R.id.operation_volume_brightness); mOperationBg = (ImageView) findViewById(R.id.operation_bg); mOperationPercent = (ImageView) findViewById(R.id.operation_percent); mLoadingView = findViewById(R.id.video_loading); // View view = findViewById(R.id.media_player_layout); // mVideoView.setView(view); // ~~~ 绑定事件 mVideoView.setOnCompletionListener(this); mVideoView.setOnInfoListener(this); mDefaultImg = (ImageView) findViewById(R.id.media_player_image); // ~~~ 绑定数据 mAudioManager = (AudioManager) getSystemService(Context.AUDIO_SERVICE); mMaxVolume = mAudioManager.getStreamMaxVolume(AudioManager.STREAM_MUSIC); if (mPath.startsWith("http:")) mVideoView.setVideoURI(Uri.parse(mPath)); else mVideoView.setVideoPath(mPath); //设置显示名称 mMediaController = new MediaController(this); mMediaController.setFileName(mTitle); mVideoView.setMediaController(mMediaController); mVideoView.requestFocus(); mGestureDetector = new GestureDetector(this, new MyGestureListener()); setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE); } @Override protected void onPause() { super.onPause(); if (mVideoView != null) mVideoView.pause(); } @Override protected void onResume() { super.onResume(); if (mVideoView != null) mVideoView.resume(); } @Override protected void onDestroy() { super.onDestroy(); if (mVideoView != null) mVideoView.stopPlayback(); } @Override public boolean onTouchEvent(MotionEvent event) { if (mGestureDetector.onTouchEvent(event)) return true; // 处理手势结束 switch (event.getAction() & MotionEvent.ACTION_MASK) { case MotionEvent.ACTION_UP: endGesture(); break; } return super.onTouchEvent(event); } /** 手势结束 */ private void endGesture() { mVolume = -1; mBrightness = -1f; // 隐藏 mDismissHandler.removeMessages(0); mDismissHandler.sendEmptyMessageDelayed(0, 500); } private class MyGestureListener extends SimpleOnGestureListener { /** 双击 */ @Override public boolean onDoubleTap(MotionEvent e) { if (mLayout == VideoView.VIDEO_LAYOUT_ZOOM) mLayout = VideoView.VIDEO_LAYOUT_ORIGIN; else mLayout++; if (mVideoView != null) mVideoView.setVideoLayout(mLayout, 0); return true; } /** 滑动 */ @Override public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) { float mOldX = e1.getX(), mOldY = e1.getY(); int y = (int) e2.getRawY(); Display disp = getWindowManager().getDefaultDisplay(); int windowWidth = disp.getWidth(); int windowHeight = disp.getHeight(); if (mOldX > windowWidth * 4.0 / 5)// 右边滑动 onVolumeSlide((mOldY - y) / windowHeight); else if (mOldX < windowWidth / 5.0)// 左边滑动 onBrightnessSlide((mOldY - y) / windowHeight); return super.onScroll(e1, e2, distanceX, distanceY); } } /** 定时隐藏 */ private Handler mDismissHandler = new Handler() { @Override public void handleMessage(Message msg) { mVolumeBrightnessLayout.setVisibility(View.GONE); } }; /** * 滑动改变声音大小 * * @param percent */ private void onVolumeSlide(float percent) { if (mVolume == -1) { mVolume = mAudioManager.getStreamVolume(AudioManager.STREAM_MUSIC); if (mVolume < 0) mVolume = 0; // 显示 mOperationBg.setImageResource(R.drawable.video_volumn_bg); mVolumeBrightnessLayout.setVisibility(View.VISIBLE); } int index = (int) (percent * mMaxVolume) + mVolume; if (index > mMaxVolume) index = mMaxVolume; else if (index < 0) index = 0; // 变更声音 mAudioManager.setStreamVolume(AudioManager.STREAM_MUSIC, index, 0); // 变更进度条 ViewGroup.LayoutParams lp = mOperationPercent.getLayoutParams(); lp.width = findViewById(R.id.operation_full).getLayoutParams().width * index / mMaxVolume; mOperationPercent.setLayoutParams(lp); } /** * 滑动改变亮度 * * @param percent */ private void onBrightnessSlide(float percent) { if (mBrightness < 0) { mBrightness = getWindow().getAttributes().screenBrightness; if (mBrightness <= 0.00f) mBrightness = 0.50f; if (mBrightness < 0.01f) mBrightness = 0.01f; // 显示 mOperationBg.setImageResource(R.drawable.video_brightness_bg); mVolumeBrightnessLayout.setVisibility(View.VISIBLE); } WindowManager.LayoutParams lpa = getWindow().getAttributes(); lpa.screenBrightness = mBrightness + percent; if (lpa.screenBrightness > 1.0f) lpa.screenBrightness = 1.0f; else if (lpa.screenBrightness < 0.01f) lpa.screenBrightness = 0.01f; getWindow().setAttributes(lpa); ViewGroup.LayoutParams lp = mOperationPercent.getLayoutParams(); lp.width = (int) (findViewById(R.id.operation_full).getLayoutParams().width * lpa.screenBrightness); mOperationPercent.setLayoutParams(lp); } @Override public void onConfigurationChanged(Configuration newConfig) { if (mVideoView != null) mVideoView.setVideoLayout(mLayout, 0); super.onConfigurationChanged(newConfig); } @Override public void onCompletion(MediaPlayer player) { finish(); } private void stopPlayer() { if (mVideoView != null) mVideoView.pause(); } private void startPlayer() { if (mVideoView != null) mVideoView.start(); } private boolean isPlaying() { return mVideoView != null && mVideoView.isPlaying(); } /** 是否需要自动恢复播放,用于自动暂停,恢复播放 */ private boolean needResume; @Override public boolean onInfo(MediaPlayer arg0, int arg1, int arg2) { switch (arg1) { case MediaPlayer.MEDIA_INFO_BUFFERING_START: //开始缓存,暂停播放 if (isPlaying()) { stopPlayer(); needResume = true; } mDefaultImg.setVisibility(View.GONE); mLoadingView.setVisibility(View.VISIBLE); break; case MediaPlayer.MEDIA_INFO_BUFFERING_END: //缓存完成,继续播放 if (needResume) startPlayer(); mLoadingView.setVisibility(View.GONE); break; case MediaPlayer.MEDIA_INFO_DOWNLOAD_RATE_CHANGED: //显示 下载速度 Log.e("","download rate:" + arg2); //mListener.onDownloadRateChanged(arg2); break; } return true; } }接下来是xml布局controller<?xml version="1.0" encoding="utf-8"?> <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="680dp" android:layout_height="74dip" android:layout_marginBottom="0.0dip" android:layout_marginLeft="0.0dip" android:layout_marginRight="0.0dip" android:layout_alignParentBottom="true" android:layout_gravity="center_horizontal" android:background="@drawable/mediacontroller_bg" android:orientation="horizontal" > <!-- android:layout_alignParentBottom="true" --> <!-- android:layout_gravity="center" --> <ImageButton android:id="@+id/mediacontroller_play_pause" android:layout_width="54.6dip" android:layout_height="32dip" android:layout_alignParentRight="true" android:layout_centerHorizontal="true" android:layout_centerVertical="true" android:layout_marginRight="7.0dip" android:background="#00000000" android:contentDescription="@string/mediacontroller_play_pause" android:gravity="center" android:src="@drawable/mediacontroller_pause_button" /> <RelativeLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_marginBottom="2dip" android:layout_marginLeft="7.0dip" android:layout_marginRight="7.0dip" android:layout_marginTop="2dip" android:layout_toLeftOf="@+id/mediacontroller_play_pause" > <RelativeLayout android:layout_width="680dp" android:layout_height="wrap_content" > <TextView android:id="@+id/mediacontroller_time_current" style="@style/MediaController_Text" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_alignParentLeft="true" android:layout_alignParentTop="true" /> <TextView android:id="@+id/mediacontroller_time_total" style="@style/MediaController_Text" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_alignParentRight="true" android:layout_alignParentTop="true" /> </RelativeLayout> <SeekBar android:id="@+id/mediacontroller_seekbar" style="@style/MediaController_SeekBar" android:layout_width="fill_parent" android:layout_height="wrap_content" android:layout_centerVertical="true" android:focusable="true" android:max="1000" /> <TextView android:id="@+id/mediacontroller_file_name" style="@style/MediaController_Text" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_alignParentBottom="true" android:ellipsize="marquee" android:singleLine="true" /> </RelativeLayout> </RelativeLayout>
activity
<?xml version="1.0" encoding="utf-8"?> <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="680dp" android:layout_height="wrap_content" android:layout_gravity="center" > <RelativeLayout android:id="@+id/media_player_layout" android:layout_width="680dp" android:layout_height="561dp" android:layout_alignParentTop="true" android:background="@drawable/media_player_bg" > <com.example.videotest.widget.VideoView android:id="@+id/surface_view" android:layout_width="626dp" android:layout_height="470dp" android:layout_centerHorizontal="true" android:layout_marginTop="20dp" /> <ImageView android:id="@+id/media_player_image" android:layout_width="626dp" android:layout_height="470dp" android:layout_centerHorizontal="true" android:layout_marginTop="20dp" android:background="@drawable/ic_launcher" /> <!-- <include android:id="@+id/Controller" layout="@layout/mediacontroller" android:layout_alignParentBottom="true"/> --> </RelativeLayout> <LinearLayout android:id="@+id/video_loading" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_centerInParent="true" android:gravity="center_vertical" android:orientation="horizontal" > <ProgressBar android:layout_width="wrap_content" android:layout_height="wrap_content" /> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:paddingTop="7.0dip" android:text="@string/video_layout_loading" android:textColor="#ffffff" android:textSize="20.0sp" /> </LinearLayout> <FrameLayout android:id="@+id/operation_volume_brightness" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_centerInParent="true" android:background="#00000000" android:orientation="horizontal" android:padding="0dip" android:visibility="invisible" > <ImageView android:id="@+id/operation_bg" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="center" android:src="@drawable/video_volumn_bg" /> <FrameLayout android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="bottom|center_horizontal" android:paddingBottom="25dip" > <ImageView android:id="@+id/operation_full" android:layout_width="94dip" android:layout_height="wrap_content" android:layout_gravity="left" android:src="@drawable/video_num_bg" /> <ImageView android:id="@+id/operation_percent" android:layout_width="0dip" android:layout_height="wrap_content" android:layout_gravity="left" android:scaleType="matrix" android:src="@drawable/video_num_front" /> </FrameLayout> </FrameLayout> </RelativeLayout>manifast文件做如下配置
<activity android:name="com.example.videotest.VideoPlayActivity" android:configChanges="orientation|keyboard|keyboardHidden|navigation" android:label="@string/app_name" android:theme="@android:style/Theme.Translucent.NoTitleBar"> <intent-filter> <action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.LAUNCHER" /> </intent-filter> <!-- android:theme="@android:style/Theme.NoTitleBar.Fullscreen" --> <!-- android:theme="@android:style/Theme.Translucent.NoTitleBar" --> <intent-filter> <action android:name="android.intent.action.VIEW" /> <category android:name="android.intent.category.LAUNCHER" /> <category android:name="android.intent.category.DEFAULT" /> <category android:name="android.intent.category.BROWSABLE" /> <data android:mimeType="audio/*" android:scheme="http" /> <data android:mimeType="audio/*" android:scheme="rtsp" /> <data android:mimeType="audio/*" android:scheme="rtmp" /> <data android:mimeType="audio/*" android:scheme="udp" /> <data android:mimeType="audio/*" android:scheme="tcp" /> <data android:mimeType="audio/*" android:scheme="file" /> <data android:mimeType="audio/*" android:scheme="content" /> <data android:mimeType="audio/*" android:scheme="mms" /> <data android:mimeType="video/*" android:scheme="http" /> <data android:mimeType="video/*" android:scheme="rtsp" /> <data android:mimeType="video/*" android:scheme="rtmp" /> <data android:mimeType="video/*" android:scheme="udp" /> <data android:mimeType="video/*" android:scheme="tcp" /> <data android:mimeType="video/*" android:scheme="file" /> <data android:mimeType="video/*" android:scheme="content" /> <data android:mimeType="video/*" android:scheme="mms" /> <data android:mimeType="application/octet-stream" /> <data android:mimeType="application/x-mpegurl" /> <data android:mimeType="application/vnd.apple.mpegurl" /> </intent-filter> <intent-filter> <action android:name="android.intent.action.VIEW" /> <category android:name="android.intent.category.DEFAULT" /> <data android:scheme="content" /> </intent-filter> <intent-filter> <action android:name="android.intent.action.VIEW" /> <category android:name="android.intent.category.DEFAULT" /> <category android:name="android.intent.category.BROWSABLE" /> <data android:mimeType="application/x-mpegurl" android:scheme="http" /> </intent-filter> <intent-filter> <action android:name="android.intent.action.VIEW" /> <category android:name="android.intent.category.DEFAULT" /> <category android:name="android.intent.category.BROWSABLE" /> <data android:scheme="rtsp" /> <data android:scheme="rtmp" /> <data android:scheme="mms" /> <data android:scheme="tcp" /> <data android:scheme="udp" /> </intent-filter> <intent-filter> <action android:name="android.intent.action.VIEW" /> <action android:name="android.intent.action.SEND" /> <action android:name="android.intent.action.SENDTO" /> <category android:name="android.intent.category.DEFAULT" /> <data android:mimeType="video/*" /> <data android:mimeType="application/sdp" /> <data android:mimeType="application/octet-stream" /> </intent-filter> <intent-filter> <action android:name="android.intent.action.VIEW" /> <category android:name="android.intent.category.DEFAULT" /> <category android:name="android.intent.category.BROWSABLE" /> <data android:scheme="http" /> <data android:mimeType="video/*" /> </intent-filter> <intent-filter> <action android:name="android.intent.action.VIEW" /> <category android:name="android.intent.category.DEFAULT" /> <category android:name="android.intent.category.BROWSABLE" /> <data android:scheme="file" /> <data android:scheme="content" /> <data android:scheme="http" /> <data android:scheme="https" /> <data android:scheme="ftp" /> <data android:scheme="rtsp" /> <data android:scheme="rtmp" /> <data android:scheme="mms" /> <data android:scheme="tcp" /> <data android:scheme="udp" /> <data android:scheme="gopher" /> <data android:mimeType="video/*" /> <data android:mimeType="audio/*" /> </intent-filter> <intent-filter> <action android:name="android.intent.action.VIEW" /> <category android:name="android.intent.category.DEFAULT" /> <category android:name="android.intent.category.BROWSABLE" /> <data android:scheme="file" /> <data android:scheme="content" /> <data android:scheme="http" /> <data android:scheme="https" /> <data android:scheme="ftp" /> <data android:scheme="rtsp" /> <data android:scheme="rtmp" /> <data android:scheme="mms" /> <data android:scheme="tcp" /> <data android:scheme="udp" /> <data android:scheme="gopher" /> <data android:host="*" /> <data android:pathPattern=".*\\.avi" /> <data android:pathPattern=".*\\.asf" /> <data android:pathPattern=".*\\.f4v" /> <data android:pathPattern=".*\\.flv" /> <data android:pathPattern=".*\\.mkv" /> <data android:pathPattern=".*\\.mpeg" /> <data android:pathPattern=".*\\.mpg" /> <data android:pathPattern=".*\\.mov" /> <data android:pathPattern=".*\\.rm" /> <data android:pathPattern=".*\\.vob" /> <data android:pathPattern=".*\\.wmv" /> <data android:pathPattern=".*\\.ts" /> <data android:pathPattern=".*\\.tp" /> <data android:pathPattern=".*\\.m3u" /> <data android:pathPattern=".*\\.m3u8" /> <data android:pathPattern=".*\\.m4v" /> <data android:pathPattern=".*\\.mp4" /> <data android:pathPattern=".*\\.swf" /> <data android:pathPattern=".*\\.rmvb" /> </intent-filter> </activity>配置支持哪些视频。
完整的demo不知道怎么上传,到时候上传,贴上下载地址。
运行截图:
视频大小大家可以控制,我这里就直接全屏了。
感谢大家阅读。
下载地址:点击打开链接