天天看点

Android实现视频播放的3种实现方式

Android提供了常见的视频的编码、解码机制。使用Android自带的MediaPlayer、MediaController等类可以很方便的实现视频播放的功能。支持的视频格式有MP4和3GP等。这些多媒体数据可以来自于Android应用的资源文件,也可以来自于外部存储器上的文件,甚至可以是来自于网络上的文件流。

下面来说一下视频播放的几种实现方式:

1、MediaController+VideoView实现方式

这种方式是最简单的实现方式。VideoView继承了SurfaceView同时实现了MediaPlayerControl接口,MediaController则是安卓封装的辅助控制器,带有暂停,播放,停止,进度条等控件。通过VideoView+MediaController可以很轻松的实现视频播放、停止、快进、快退等功能。

布局文件如下:

<?xml version="1.0" encoding="utf-8"?>

<android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"

xmlns:app="http://schemas.android.com/apk/res-auto"

xmlns:tools="http://schemas.android.com/tools"

android:layout_width="match_parent"

android:layout_height="match_parent"

tools:context=".VideoViewTestActivity">

<VideoView

android:id="@+id/videoView"

android:layout_width="match_parent"

android:layout_height="match_parent" />

</android.support.constraint.ConstraintLayout>

程序代码如下:

public class VideoViewTestActivity extends AppCompatActivity {

@Override

protected void onCreate(Bundle savedInstanceState) {

super.onCreate(savedInstanceState);

setContentView(R.layout.activity_video_view_test);

VideoView videoView = (VideoView)findViewById(R.id.videoView);

//加载指定的视频文件

String path = Environment.getExternalStorageDirectory().getPath()+"/20180730.mp4";

videoView.setVideoPath(path);

//创建MediaController对象

MediaController mediaController = new MediaController(this);

//VideoView与MediaController建立关联

videoView.setMediaController(mediaController);

//让VideoView获取焦点

videoView.requestFocus();

}

}

使用此实现方式的步骤:

加载指定的视频文件

建立VideoView和MediaController之间的关联,这样就不需要自己去控制视频的播放、暂停等。让MediaController控制即可。

VideoView获取焦点。

实现效果图如下:

界面中的快退、播放、快进、时间、进度条等是由MediaController提供的。

2、MediaPlayer+SurfaceView+自定义控制器

虽然VideoView的实现方式很简单,但是由于是自带的封装好的类,所以无论是播放器的大小、位置以及控制都不受我们控制。

这种实现方式步骤如下:

创建MediaPlayer对象,并让它加载指定的视频文件。可以是应用的资源文件、本地文件路径、或者URL。

在界面布局文件中定义SurfaceView组件,并为SurfaceView的SurfaceHolder添加Callback监听器。

调用MediaPlayer对象的setDisplay(SurfaceHolder sh)将所播放的视频图像输出到指定的SurfaceView组件。

调用MediaPlayer对象的prepareAsync()或prepare()方法装载流媒体文件

调用MediaPlayer对象的start()、stop()和pause()方法来控制视频的播放。

在实现第二步之前需要先给surfaceHolder设置一个callback,callback的3个回调函数如下:

@Override

public void surfaceCreated(SurfaceHolder holder) {

public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {

public void surfaceDestroyed(SurfaceHolder holder) {

tools:context=".PlayVideoActivity">

<RelativeLayout

android:id="@+id/root_rl"

android:layout_height="400dp"

android:background="#000000">

<SurfaceView

android:id="@+id/surfaceView"

android:layout_width="match_parent"

android:layout_height="400dp" />

<ImageView

android:id="@+id/playOrPause"

android:layout_width="60dp"

android:layout_height="60dp"

android:layout_centerInParent="true"

android:src="@android:drawable/ic_media_play"/>

<LinearLayout

android:id="@+id/control_ll"

android:layout_height="wrap_content"

android:layout_alignParentBottom="true"

android:background="#005500"

android:orientation="vertical">

<RelativeLayout

android:layout_width="match_parent"

android:layout_height="wrap_content"

android:layout_marginLeft="10dp"

android:layout_marginRight="10dp"

android:orientation="horizontal"

android:paddingBottom="5dp">

<TextView

android:id="@+id/tv_start_time"

android:layout_width="wrap_content"

android:layout_height="wrap_content"

android:layout_alignParentLeft="true"

android:layout_marginLeft="30dp"

android:text="00.00"

android:textColor="#ffffff"/>

android:id="@+id/tv_separate_time"

android:layout_toRightOf="@+id/tv_start_time"

android:layout_marginLeft="1dp"

android:text="/"

android:id="@+id/tv_end_time"

android:layout_toRightOf="@+id/tv_separate_time"

<ImageView

android:id="@+id/tv_backward"

android:layout_below="@+id/tv_start_time"

android:src="@android:drawable/ic_media_rew"/>

<SeekBar

android:id="@+id/tv_progess"

android:layout_width="match_parent"

android:layout_toRightOf="@+id/tv_backward"

android:layout_toLeftOf="@+id/tv_forward"

android:layout_below="@+id/tv_start_time"/>

android:id="@+id/tv_forward"

android:layout_alignParentRight="true"

android:layout_marginRight="1dp"

android:src="@android:drawable/ic_media_ff"/>

</RelativeLayout>

</LinearLayout>

</RelativeLayout>

public class PlayVideoActivity extends AppCompatActivity implements SurfaceHolder.Callback,

MediaPlayer.OnPreparedListener,

MediaPlayer.OnCompletionListener,

MediaPlayer.OnErrorListener,

MediaPlayer.OnInfoListener, View.OnClickListener,

MediaPlayer.OnSeekCompleteListener,

MediaPlayer.OnVideoSizeChangedListener,

SeekBar.OnSeekBarChangeListener,

{

private ImageView playOrPauseIv;

private SurfaceView videoSuf;

private MediaPlayer mPlayer;

private SeekBar mSeekBar;

private String path;

private RelativeLayout rootViewRl;

private LinearLayout controlLl;

private TextView startTime, endTime;

private ImageView forwardButton, backwardButton;

private boolean isShow = false;

public static final int UPDATE_TIME = 0x0001;

public static final int HIDE_CONTROL = 0x0002;

private Handler mHandler = new Handler() {

@Override

public void handleMessage(Message msg) {

switch (msg.what) {

case UPDATE_TIME:

updateTime();

mHandler.sendEmptyMessageDelayed(UPDATE_TIME, 500);

break;

case HIDE_CONTROL:

hideControl();

}

}

};

setContentView(R.layout.activity_play_video);

initViews();

initData();

initSurfaceView();

initPlayer();

initEvent();

private void initData() {

path = Environment.getExternalStorageDirectory().getPath() + "/20180730.mp4";//这里写上你的视频地址

private void initEvent() {

playOrPauseIv.setOnClickListener(this);

rootViewRl.setOnClickListener(this);

rootViewRl.setOnTouchListener(this);

forwardButton.setOnClickListener(this);

backwardButton.setOnClickListener(this);

mSeekBar.setOnSeekBarChangeListener(this);

private void initSurfaceView() {

videoSuf = (SurfaceView) findViewById(R.id.surfaceView);

videoSuf.setZOrderOnTop(false);

videoSuf.getHolder().setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);

videoSuf.getHolder().addCallback(this);

private void initPlayer() {

mPlayer = new MediaPlayer();

mPlayer.setOnCompletionListener(this);

mPlayer.setOnErrorListener(this);

mPlayer.setOnInfoListener(this);

mPlayer.setOnPreparedListener(this);

mPlayer.setOnSeekCompleteListener(this);

mPlayer.setOnVideoSizeChangedListener(this);

try {

//使用手机本地视频

mPlayer.setDataSource(path);

} catch (Exception e) {

e.printStackTrace();

private void initViews() {

playOrPauseIv = (ImageView) findViewById(R.id.playOrPause);

startTime = (TextView) findViewById(R.id.tv_start_time);

endTime = (TextView) findViewById(R.id.tv_end_time);

mSeekBar = (SeekBar) findViewById(R.id.tv_progess);

rootViewRl = (RelativeLayout) findViewById(R.id.root_rl);

controlLl = (LinearLayout) findViewById(R.id.control_ll);

forwardButton = (ImageView) findViewById(R.id.tv_forward);

backwardButton = (ImageView) findViewById(R.id.tv_backward);

public void surfaceCreated(SurfaceHolder holder) {

mPlayer.setDisplay(holder);

mPlayer.prepareAsync();

public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {

public void surfaceDestroyed(SurfaceHolder holder) {

public void onPrepared(MediaPlayer mp) {

startTime.setText(FormatTimeUtil.formatLongToTimeStr(mp.getCurrentPosition()));

endTime.setText(FormatTimeUtil.formatLongToTimeStr(mp.getDuration()));

mSeekBar.setMax(mp.getDuration());

mSeekBar.setProgress(mp.getCurrentPosition());

public void onCompletion(MediaPlayer mp) {

public boolean onError(MediaPlayer mp, int what, int extra) {

return false;

public boolean onInfo(MediaPlayer mp, int what, int extra) {

private void play() {

if (mPlayer == null) {

return;

Log.i("playPath", path);

if (mPlayer.isPlaying()) {

mPlayer.pause();

mHandler.removeMessages(UPDATE_TIME);

mHandler.removeMessages(HIDE_CONTROL);

playOrPauseIv.setVisibility(View.VISIBLE);

playOrPauseIv.setImageResource(android.R.drawable.ic_media_play);

} else {

mPlayer.start();

mHandler.sendEmptyMessageDelayed(UPDATE_TIME, 500);

mHandler.sendEmptyMessageDelayed(HIDE_CONTROL, 5000);

playOrPauseIv.setVisibility(View.INVISIBLE);

playOrPauseIv.setImageResource(android.R.drawable.ic_media_pause);

public void onSeekComplete(MediaPlayer mp) {

//TODO

public void onVideoSizeChanged(MediaPlayer mp, int width, int height) {

public void onClick(View v) {

switch (v.getId()) {

case R.id.tv_backward:

backWard();

break;

case R.id.tv_forward:

forWard();

case R.id.playOrPause:

play();

case R.id.root_rl:

showControl();

/**

* 更新播放时间

*/

private void updateTime() {

startTime.setText(FormatTimeUtil.formatLongToTimeStr(

mPlayer.getCurrentPosition()));

mSeekBar.setProgress(mPlayer.getCurrentPosition());

* 隐藏进度条

private void hideControl() {

isShow = false;

mHandler.removeMessages(UPDATE_TIME);

controlLl.animate().setDuration(300).translationY(controlLl.getHeight());

* 显示进度条

private void showControl() {

if (isShow) {

play();

isShow = true;

mHandler.removeMessages(HIDE_CONTROL);

mHandler.sendEmptyMessage(UPDATE_TIME);

mHandler.sendEmptyMessageDelayed(HIDE_CONTROL, 5000);

controlLl.animate().setDuration(300).translationY(0);

* 设置快进10秒方法

private void forWard(){

if(mPlayer != null){

int position = mPlayer.getCurrentPosition();

mPlayer.seekTo(position + 10000);

* 设置快退10秒的方法

public void backWard(){

if(position > 10000){

position-=10000;

}else{

position = 0;

mPlayer.seekTo(position);

//OnSeekBarChangeListener

public void onProgressChanged(SeekBar seekBar, int progress, boolean b) {

if(mPlayer != null && b){

mPlayer.seekTo(progress);

public void onStartTrackingTouch(SeekBar seekBar) {

public void onStopTrackingTouch(SeekBar seekBar) {

注意事项:MediaPlayer有prepare和prepareAsync两种方法。这两种方法的区别是:prepare方法是将资源同步缓存到内存中,一般加载本地较小的资源可以用这个,如果是较大的资源或者网络资源建议使用prepareAsync方法,异步加载。

实现效果如下所示:

3、MediaPlayer+SurfaceView+MediaController

第二种实现方式使用的是自定义控件,MediaPlayer+SurfaceView也可以使用系统自带的MediaController控制器。

使用这个方式实现,布局文件只需一个SurfaceView即可,其他的控件都交给MediaController控制器,布局文件如下:

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"

tools:context=".MediaControllerTestActivity"

android:id="@+id/root_ll">

<SurfaceView

android:layout_height="match_parent"

android:id="@+id/controll_surfaceView"/>

</LinearLayout>

public class MediaControllerTestActivity extends Activity implements

MediaController.MediaPlayerControl,

MediaPlayer.OnBufferingUpdateListener,

SurfaceHolder.Callback{

private MediaPlayer mediaPlayer;

private MediaController controller;

private int bufferPercentage = 0;

setContentView(R.layout.activity_media_controller_test);

mediaPlayer = new MediaPlayer();

controller = new MediaController(this);

controller.setAnchorView(findViewById(R.id.root_ll));

SurfaceView videoSuf = (SurfaceView) findViewById(R.id.controll_surfaceView);

protected void onResume() {

super.onResume();

String path = Environment.getExternalStorageDirectory().getPath() + "/20180730.mp4";

mediaPlayer.setDataSource(path);

mediaPlayer.setOnBufferingUpdateListener(this);

//mediaPlayer.prepare();

controller.setMediaPlayer(this);

controller.setEnabled(true);

}catch (IOException e){

protected void onPause() {

super.onPause();

if (mediaPlayer.isPlaying()){

mediaPlayer.stop();

protected void onDestroy() {

super.onDestroy();

if (null != mediaPlayer){

mediaPlayer.release();

mediaPlayer = null;

public boolean onTouchEvent(MotionEvent event) {

controller.show();

return super.onTouchEvent(event);

//MediaPlayer

public void onPointerCaptureChanged(boolean hasCapture) {

//MediaPlayerControl

public void onBufferingUpdate(MediaPlayer mediaPlayer, int i) {

bufferPercentage = i;

public void start() {

mediaPlayer.start();

public void pause() {

mediaPlayer.pause();

public int getDuration() {

return mediaPlayer.getDuration();

public int getCurrentPosition() {

return mediaPlayer.getCurrentPosition();

public void seekTo(int i) {

mediaPlayer.seekTo(i);

public boolean isPlaying() {

return true;

public int getBufferPercentage() {

return bufferPercentage;

public boolean canPause() {

return true;

public boolean canSeekBackward() {

public boolean canSeekForward() {

public int getAudioSessionId() {

return 0;

//SurfaceHolder.callback

public void surfaceCreated(SurfaceHolder surfaceHolder) {

mediaPlayer.setDisplay(surfaceHolder);

mediaPlayer.prepareAsync();

public void surfaceChanged(SurfaceHolder surfaceHolder, int i, int i1, int i2) {

public void surfaceDestroyed(SurfaceHolder surfaceHolder) {