目前很多app都会有短视频内容,这里就来讲一下android中播放视频的几种方式。
Android播放视频有三种方式:
1,调用系统已有的播放软件播放视频。
2,使用android提供的VideoView控件定制自己的视频播放器。
3,使用MediaPlayer和SurfaceView定制自己的视频播放器。
第一种方式最简单了:
//调用系统自带的播放器 Uri uri = Uri.parse("/storage/emulated/0/DCIM/Camera/VID_20161103_105921.mp4"); Intent intent = new Intent(Intent.ACTION_VIEW); intent.setDataAndType(uri, "video/mp4"); startActivity(intent);
/packages/apps/Gallery2
通过清单文件,可以知道处理该intent的activity:
看一下这个MovieActivity.java
@Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); 。。 setContentView(R.layout.movie_view); View rootView = findViewById(R.id.movie_view_root); setSystemUiVisibility(rootView); Intent intent = getIntent(); 。。。 mPlayer = new MoviePlayer(rootView, this, intent.getData(), savedInstanceState, !mFinishOnCompletion) { @Override public void onCompletion() { if (mFinishOnCompletion) { finish(); } } };
主要用到MoviePlayer,看一下这个MoviePlayer类做了啥
public MoviePlayer(View rootView, final MovieActivity movieActivity, Uri videoUri, Bundle savedInstance, boolean canReplay) { mContext = movieActivity.getApplicationContext(); mRootView = rootView; mVideoView = (VideoView) rootView.findViewById(R.id.surface_view); mBookmarker = new Bookmarker(movieActivity); mUri = videoUri; mController = new MovieControllerOverlay(mContext); ((ViewGroup)rootView).addView(mController.getView()); mController.setListener(this); mController.setCanReplay(canReplay); mVideoView.setOnErrorListener(this); mVideoView.setOnCompletionListener(this); mVideoView.setVideoURI(mUri); mVideoView.setOnTouchListener(new View.OnTouchListener() { @Override public boolean onTouch(View v, MotionEvent event) { mController.show(); return true; } }); // The SurfaceView is transparent before drawing the first frame. // This makes the UI flashing when open a video. (black -> old screen // -> video) However, we have no way to know the timing of the first // frame. So, we hide the VideoView for a while to make sure the // video has been drawn on it. mVideoView.postDelayed(new Runnable() { @Override public void run() { mVideoView.setVisibility(View.VISIBLE);
那么接着就讲一下第二种方式,使用VideoView定制自己的视频播放器
VideoView看名字应该是View的子类
VideoView的直接父类是SurfaceView .关于SurfaceView前面Android视图SurfaceView的使用一文已做简单介绍。
SurfaceView可以用来显示相机的预览界面,也可以用来显示视频的数据。
下面就来看一下如何使用VideoView。
使用VideoView播放视频也有两种方式:
一种是只使用VideoView进行播放视频,自己自定义播放进度,播放状态的布局,然后自己控制播放
另一种就是VideoView结合MediaController播放视频,这种方式不需要自己写布局去控制播放。
下面先看第一方式:
结合代码和效果图一起分析下是如何实现播放视频的:
用手机拍摄了一段视频,然后用写的应用程序来播放这段视频(真机测试的)
启动应用看到界面如上:
看一下布局文件:
<framelayout android:id="@+id/activity_main" android:layout_height="match_parent" android:layout_width="match_parent" tools:context="cj.com.videoviewdemo2.MainActivity" xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools"></framelayout> <framelayout android:alpha="0" android:id="@+id/cover" android:layout_height="match_parent" android:layout_width="match_parent"></framelayout>
下面的代码片段就是设置视频预览图片
private void initVideoView() { Log.d(TAG,"initVideoView "); Bitmap bitmap = ThumbnailUtils.createVideoThumbnail(PATH, MINI_KIND); if(bitmap != null){ preview.setImageBitmap(bitmap); } videoView.setVideoURI(Uri.parse(PATH)); }这里用到了ThumbnailUtils这个工具,参考官方文档:https://developer.android.com/reference/android/media/ThumbnailUtils.html
VideoView播放视频,需要传入视频资源,传入方式有以下几种:
关于VideoView官方文档:https://developer.android.com/reference/android/widget/VideoView.html
我这里播放本地视频,当然也可以播放网络视频。
点击CheckBox进行播放,看一下播放效果:
看一下代码:
playOrPause.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() { @Override public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) { Log.d(TAG,"onCheckedChanged isChecked="+isChecked); if(isChecked){//暂停 videoView.pause(); positon = videoView.getCurrentPosition(); }else{//播放 videoView.seekTo(positon); videoView.start(); playOrPause.setVisibility(View.GONE); preview.setVisibility(View.GONE); } } });
pasue()暂停播放,getCurrentPosition()获取播放到哪个位置了 时间毫秒。
看一下控制播放进度消息的代码:
@Override public void onClick(View v) { switch (v.getId()){ case R.id.cover: Log.d(TAG,"click cover "); int currentPosition = videoView.getCurrentPosition(); int duration = videoView.getDuration(); Log.d(TAG,"currentPosition = "+currentPosition+"duration = "+duration); if(currentPosition>0){//说明已经播放了,不管现在是暂停还是播放中 if(playOrPause.getVisibility() == View.VISIBLE){ playOrPause.setVisibility(View.GONE); }else { playOrPause.setVisibility(View.VISIBLE); } if(progressLayout.getVisibility() == View.VISIBLE){ myHandler.removeCallbacks(runnable); progressLayout.setVisibility(View.GONE); }else { progressLayout.setVisibility(View.VISIBLE); currentTime.setText((currentPosition/1000)/60%60/10+""+(currentPosition/1000)/60%60%10+":" +(currentPosition/1000)%60/10+""+(currentPosition/1000)%60%10); sumTime.setText((duration/1000)/60%60/10+""+(duration/1000)/60%60%10+":" +(duration/1000)%60/10+""+(duration/1000)%60%10); progressBar.setMax(duration); progressBar.setProgress(currentPosition); new Thread(new Runnable() { @Override public void run() { while(progressLayout.getVisibility()==View.VISIBLE){ myHandler.sendEmptyMessage(0); try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } } } }).start(); myHandler.postDelayed(runnable,3000); } } break; default: break; } } private Runnable runnable = new Runnable() { @Override public void run() { playOrPause.setVisibility(View.GONE); progressLayout.setVisibility(View.GONE); } };
接着看一下VideoView结合MediaController来播放视频,就简单几句代码就可以了:
videoView = (MyVideoView) findViewById(R.id.video_view); videoView.setVideoURI(Uri.parse(PATH)); MediaController mediaController = new MediaController(this); videoView.setMediaController(mediaController);布局文件
<framelayout android:id="@+id/activity_main" android:layout_height="match_parent" android:layout_width="match_parent" tools:context="cj.com.videoviewdemo3.MainActivity" xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools">效果图如下:</framelayout>
可以直接控制视频的播放了。
这里来简单看一下VideoView的源码。
源码位置:frameworks/base/core/java/android/widget/VideoView.java
public VideoView(Context context, AttributeSet attrs) { this(context, attrs, 0); initVideoView(); }
private void initVideoView() { mVideoWidth = 0; mVideoHeight = 0; getHolder().addCallback(mSHCallback); getHolder().setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS); setFocusable(true); setFocusableInTouchMode(true); requestFocus(); mCurrentState = STATE_IDLE; mTargetState = STATE_IDLE; }
public void setVideoURI(Uri uri) { setVideoURI(uri, null); }
public void setVideoURI(Uri uri, Mapheaders) { mUri = uri; mHeaders = headers; mSeekWhenPrepared = 0; openVideo(); requestLayout(); invalidate(); }
private void openVideo() { if (mUri == null || mSurfaceHolder == null) { // not ready for playback just yet, will try again later return; } // Tell the music playback service to pause // TODO: these constants need to be published somewhere in the framework. Intent i = new Intent("com.android.music.musicservicecommand"); i.putExtra("command", "pause"); mContext.sendBroadcast(i); // we shouldn't clear the target state, because somebody might have // called start() previously release(false); try { mMediaPlayer = new MediaPlayer(); mMediaPlayer.setOnPreparedListener(mPreparedListener); mMediaPlayer.setOnVideoSizeChangedListener(mSizeChangedListener); mMediaPlayer.setOnCompletionListener(mCompletionListener); mMediaPlayer.setOnErrorListener(mErrorListener); mMediaPlayer.setOnInfoListener(mOnInfoListener); mMediaPlayer.setOnBufferingUpdateListener(mBufferingUpdateListener); mCurrentBufferPercentage = 0; mMediaPlayer.setDataSource(mContext, mUri, mHeaders); mMediaPlayer.setDisplay(mSurfaceHolder); mMediaPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC); mMediaPlayer.setScreenOnWhilePlaying(true); mMediaPlayer.prepareAsync(); // we don't set the target state here either, but preserve the // target state that was there before. mCurrentState = STATE_PREPARING; attachMediaController(); } catch (IOException ex) { Log.w(TAG, "Unable to open content: " + mUri, ex); mCurrentState = STATE_ERROR; mTargetState = STATE_ERROR; mErrorListener.onError(mMediaPlayer, MediaPlayer.MEDIA_ERROR_UNKNOWN, 0); return; } catch (IllegalArgumentException ex) { Log.w(TAG, "Unable to open content: " + mUri, ex); mCurrentState = STATE_ERROR; mTargetState = STATE_ERROR; mErrorListener.onError(mMediaPlayer, MediaPlayer.MEDIA_ERROR_UNKNOWN, 0); return; } }
public void start() { if (isInPlaybackState()) { mMediaPlayer.start(); mCurrentState = STATE_PLAYING; } mTargetState = STATE_PLAYING; }
看一下MediaController这个类
它是一个视图,VideoView通过setMediaController(MediaControllercontroller)函数添加MediaController其实就是添加一个视图,该视图容器里有控件来控制视频的播放
public void setMediaController(MediaController controller) { if (mMediaController != null) { mMediaController.hide(); } mMediaController = controller; attachMediaController(); } private void attachMediaController() { if (mMediaPlayer != null && mMediaController != null) { mMediaController.setMediaPlayer(this); View anchorView = this.getParent() instanceof View ? (View)this.getParent() : this; mMediaController.setAnchorView(anchorView); mMediaController.setEnabled(isInPlaybackState()); } }
public void setAnchorView(View view) { if (mAnchor != null) { mAnchor.removeOnLayoutChangeListener(mLayoutChangeListener); } mAnchor = view; if (mAnchor != null) { mAnchor.addOnLayoutChangeListener(mLayoutChangeListener); } FrameLayout.LayoutParams frameParams = new FrameLayout.LayoutParams( ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT ); removeAllViews(); View v = makeControllerView(); addView(v, frameParams); } /** * Create the view that holds the widgets that control playback. * Derived classes can override this to create their own. * @return The controller view. * @hide This doesn't work as advertised */ protected View makeControllerView() { LayoutInflater inflate = (LayoutInflater) mContext.getSystemService(Context.LAYOUT_INFLATER_SERVICE); mRoot = inflate.inflate(com.android.internal.R.layout.media_controller, null); initControllerView(mRoot); return mRoot; }
frameworks/base/core/res/res/layout/media_controller.xml
关于VideoView播放视频就讲这些了,demo是运行在真机上的。
后面文章讲一下使用MediaPlayer和SurfaceView来播放视频。