編輯:關於Android編程
目前很多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 android:alpha="0" android:id="@+id/cover" android:layout_height="match_parent" android:layout_width="match_parent"></framelayout>
</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來播放視頻。
【Android圖像處理】給圖片加水印
一些圖像處理軟件如美圖,P圖,Prisma在處理圖像後都會給圖片加上水印。主要目的是為了從宣傳自己的產品,這個我是有切身體會的。現在我們先來看看代碼:/** * 給圖片
android批量添加聯系人
研究生期間就沒寫過Java代碼了,這幾天由於想做一個統計網頁詞頻的工具,但是使用C++不是很方便,於是又用Java做了一個小工具。翻開電腦中以前的文件,發現之前還做過一個
實例詳解Android自定義ProgressDialog進度條對話框的實現
Android SDK已經提供有進度條組件ProgressDialog組件,但用的時候我們會發現可能風格與我們應用的整體風格不太搭配,而且ProgressDialog的可
Android進階之自定義View實戰(二)九宮格手勢解鎖實現
一.引言本篇博客以九宮格手勢解鎖View為例,來說明自定義View如何根據需求處理用戶的手勢操作。雖然九宮格手勢解鎖自定義View網上資料有很多,實現原理大同小異,但這裡