前言:虽然ViedoView控件可以播放视频,但播放的位置和大小并不受我们的控制,为了对视频有更好的控制权,可以使用MediaPlayer配合SurfaceView来播放视频。
实现效果:
1、SurfaceView与MediaPlayer配合使用:
- mediaPlayer.setDisplay(surfaceview.getHolder());//这一步是关键,制定用于显示视频的SurfaceView对象(通过setDisplay())
2、维护SurfaceView:
通过sufaceView.Callback接口实现,需要实现其三个方法:
- void surfaceDestroyed(SurfaceHolder holder):当SurfaceHolder被销毁的时候回调。
- void surfaceCreated(SurfaceHolder holder):当SurfaceHolder被创建的时候回调。
- void surfaceChange(SurfaceHolder holder):当SurfaceHolder的尺寸发生变化的时候被回调。
3、通过MediaPlayer类的Play()与Pause()方法实现视频的暂停与播放。
4、对进度条的布局优化:
在drawable下定义两个xml文件,具体见以下源码。
完整源码展示:
MainActivity.Java:
- import android.app.Activity;
- import android.content.res.AssetFileDescriptor;
- import android.media.AudioManager;
- import android.media.MediaPlayer;
- import android.media.MediaPlayer.OnPreparedListener;
- import android.os.Bundle;
- import android.view.SurfaceHolder;
- import android.view.SurfaceView;
- import android.view.View;
- import android.view.View.OnClickListener;
- import android.widget.ImageButton;
- import android.widget.SeekBar;
- public class MainActivity extends Activity implements OnClickListener {
- private SurfaceView surfaceview;
- private MediaPlayer mediaPlayer;
- private ImageButton start;
- private ImageButton share;
- private ImageButton back;
- private ImageButton pause;
- private SeekBar seekBar;
- private boolean isPlaying;
- private int currentPosition = 0;
- private int postion = 0;
- @Override
- protected void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- setContentView(R.layout.activity_main);
- findViewById();
- initView();
- }
- protected void findViewById() {
- // TODO Auto-generated method stub
- surfaceview = (SurfaceView) findViewById(R.id.surfaceView);
- start = (ImageButton) findViewById(R.id.video_start);
- back = (ImageButton) findViewById(R.id.video_back);
- pause = (ImageButton) findViewById(R.id.video_pause);
- share = (ImageButton) findViewById(R.id.video_share);
- seekBar = (SeekBar) findViewById(R.id.seekBar);
- }
- protected void initView() {
- // TODO Auto-generated method stub
- mediaPlayer = new MediaPlayer();
- surfaceview.getHolder().setKeepScreenOn(true);
- surfaceview.getHolder().addCallback(new SurfaceViewLis());
- start.setOnClickListener(this);
- back.setOnClickListener(this);
- pause.setOnClickListener(this);
- share.setOnClickListener(this);
- seekBar.setOnClickListener(this);
- }
- private class SurfaceViewLis implements SurfaceHolder.Callback {
- @Override
- public void surfaceChanged(SurfaceHolder holder, int format, int width,
- int height) {
- }
- @Override
- public void surfaceCreated(SurfaceHolder holder) {
- if (currentPosition > 0) {
- // 创建SurfaceHolder的时候,如果存在上次播放的位置,则按照上次播放位置进行播放
- video_play(currentPosition);
- currentPosition = 0;
- }
- }
- @Override
- public void surfaceDestroyed(SurfaceHolder holder) {
- // 销毁SurfaceHolder的时候记录当前的播放位置并停止播放
- if (mediaPlayer != null && mediaPlayer.isPlaying()) {
- currentPosition = mediaPlayer.getCurrentPosition();
- mediaPlayer.stop();
- }
- }
- }
- @Override
- public void onClick(View v) {
- switch (v.getId()) {
- case R.id.video_start:
- video_play(0);
- break;
- case R.id.video_pause:
- pause();
- break;
- default:
- break;
- }
- }
- /**
- * 开始播放
- *
- * @param msec 播放初始位置
- */
- protected void video_play(final int msec) {
- // // 获取视频文件地址
- try {
- mediaPlayer = new MediaPlayer();
- //设置音频流类型
- mediaPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC);
- // 设置播放的视频源
- AssetFileDescriptor fd = this.getAssets().openFd("Perspective.mp4");
- mediaPlayer.setDataSource(fd.getFileDescriptor(), fd.getStartOffset(),
- fd.getLength());
- // 设置显示视频的SurfaceHolder
- mediaPlayer.setDisplay(surfaceview.getHolder());//这一步是关键,制定用于显示视频的SurfaceView对象(通过setDisplay())
- mediaPlayer.prepareAsync();
- mediaPlayer.setOnPreparedListener(new OnPreparedListener() {
- @Override
- public void onPrepared(MediaPlayer mp) {
- mediaPlayer.start();
- // 按照初始位置播放
- mediaPlayer.seekTo(msec);
- // 设置进度条的最大进度为视频流的最大播放时长
- seekBar.setMax(mediaPlayer.getDuration());
- // 开始线程,更新进度条的刻度
- new Thread() {
- @Override
- public void run() {
- try {
- isPlaying = true;
- while (isPlaying) {
- int current = mediaPlayer
- .getCurrentPosition();
- seekBar.setProgress(current);
- sleep(500);
- }
- } catch (Exception e) {
- e.printStackTrace();
- }
- }
- }.start();
- start.setEnabled(false);
- }
- });
- mediaPlayer.setOnCompletionListener(new MediaPlayer.OnCompletionListener() {
- @Override
- public void onCompletion(MediaPlayer mp) {
- // 在播放完毕被回调
- start.setEnabled(true);
- }
- });
- mediaPlayer.setOnErrorListener(new MediaPlayer.OnErrorListener() {
- @Override
- public boolean onError(MediaPlayer mp, int what, int extra) {
- // 发生错误重新播放
- video_play(0);
- isPlaying = false;
- return false;
- }
- });
- } catch (Exception e) {
- e.printStackTrace();
- }
- }
- /**
- * 暂停或继续
- */
- protected void pause() {
- if (mediaPlayer != null && mediaPlayer.isPlaying()) {
- mediaPlayer.pause();
- }
- }
- }
activity_main.xml:
- <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:tools="http://schemas.android.com/tools"
- android:layout_width="match_parent"
- android:layout_height="match_parent" >
- <FrameLayout
- android:layout_width="match_parent"
- android:layout_height="220dp">
- <SurfaceView
- android:id="@+id/surfaceView"
- android:layout_width="match_parent"
- android:layout_height="220dp"/>
- <LinearLayout
- android:layout_width="match_parent"
- android:layout_height="30dp"
- android:layout_gravity="top"
- android:orientation="horizontal"
- android:background="@drawable/video_top">
- <!--android:alpha="0.3"-->
- <ImageButton
- android:id="@+id/video_back"
- android:layout_width="15dp"
- android:layout_height="18dp"
- android:background="@drawable/video_back"
- android:layout_gravity="center"
- android:layout_marginLeft="20dp"></ImageButton>
- <ImageButton
- android:id="@+id/video_share"
- android:layout_width="25dp"
- android:layout_height="15dp"
- android:background="@drawable/video_share"
- android:layout_gravity="center"
- android:layout_marginLeft="280dp"
- />
- </LinearLayout>
- <LinearLayout
- android:layout_width="match_parent"
- android:layout_height="30dp"
- android:layout_gravity="bottom"
- android:orientation="horizontal"
- android:background="@drawable/video_bottom">
- <ImageButton
- android:id="@+id/video_start"
- android:layout_width="18dp"
- android:layout_height="wrap_content"
- android:background="@drawable/video_start"
- android:layout_gravity="center"
- android:layout_marginLeft="20dp">
- </ImageButton>
- <SeekBar
- android:id="@+id/seekBar"
- android:layout_width="250dp"
- android:layout_height="wrap_content"
- android:layout_gravity="center"
- android:layout_marginLeft="20dp"
- android:progressDrawable="@drawable/seekbar_style"
- android:thumb="@drawable/seekbar_thumb"
- android:maxHeight="8dp"
- android:minHeight="8dp"
- />
- <ImageButton
- android:id="@+id/video_pause"
- android:layout_width="15dp"
- android:layout_height="18dp"
- android:background="@drawable/video_pause"
- android:layout_gravity="center"
- android:layout_marginLeft="20dp"
- />
- </LinearLayout>
- </FrameLayout>
- </RelativeLayout>
对进度条的布局优化:
- <?xml version="1.0" encoding="utf-8"?>
- <layer-list
- xmlns:android="http://schemas.android.com/apk/res/android">
- <item >
- <shape>
- <solid android:color="#c6c6c6" />
- </shape>
- </item>
- <item>
- <clip>
- <shape>
- <solid android:color="#06a7fa" />
- </shape>
- </clip>
- </item>
- </layer-list>
对进度点的布局优化:
- <?xml version="1.0" encoding="UTF-8"?>
- <selector xmlns:android="http://schemas.android.com/apk/res/android">
- <!-- 按下状态 -->
- <item
- android:state_pressed="true"
- android:drawable="@drawable/video_seekbar_after" />
- <!-- 普通无焦点状态 -->
- <item
- android:state_focused="false"
- android:state_pressed="false"
- android:drawable="@drawable/video_seekbar_before" />
- </selector>