Android SurfaceView+MediaPlayer实现几个不同的视频轮流播放

版权声明:清晨不起早,误一天的事;幼年不勤学,误一生的事。 https://blog.csdn.net/Dr_abandon/article/details/80742058

MediaPlayer

1)如何获得MediaPlayer实例:
可以使用直接new的方式:
MediaPlayer mp = new MediaPlayer();
也可以使用create的方式,如:
MediaPlayer mp = MediaPlayer.create(this, R.raw.test);
网络或是内部存储直接setDataSource一个路径

2) 如何设置要播放的文件:
MediaPlayer要播放的文件主要包括3个来源:
a. 用户在应用中事先自带的resource资源
例如:MediaPlayer.create(this, R.raw.test);
b. 存储在SD卡或其他文件路径下的媒体文件
例如:mp.setDataSource(“/sdcard/test.mp3”);

c. 网络上的媒体文件
例如:mp.setDataSource(“http://www.citynorth.cn/music/confucius.mp3“);

MediaPlayer的setDataSource一共四个方法:
setDataSource (String path)
setDataSource (FileDescriptor fd)
setDataSource (Context context, Uri uri)
setDataSource (FileDescriptor fd, long offset, long length)

其中使用FileDescriptor时,需要将文件放到与res文件夹平级的assets文件夹里,然后使用:
AssetFileDescriptor fileDescriptor = getAssets().openFd(“rain.mp3”);
m_mediaPlayer.setDataSource(fileDescriptor.getFileDescriptor(),fileDescriptor.getStartOffset(), fileDescriptor.getLength());
来设置datasource

3)对播放器的主要控制方法:
Android通过控制播放器的状态的方式来控制媒体文件的播放,其中:
prepare()和prepareAsync() 提供了同步和异步两种方式设置播放器进入prepare状态,需要注意的是,如果MediaPlayer实例是由create方法创建的,那么第一次启动播放前不需要再调用prepare()了,因为create方法里已经调用过了。
start()是真正启动文件播放的方法,
pause()和stop()比较简单,起到暂停和停止播放的作用,

seekTo()是定位方法,可以让播放器从指定的位置开始播放,需要注意的是该方法是个异步方法,也就是说该方法返回时并不意味着定位完成,尤其是播放的网络文件,真正定位完成时会触发OnSeekComplete.onSeekComplete(),如果需要是可以调用setOnSeekCompleteListener(OnSeekCompleteListener)设置监听器来处理的。
release()可以释放播放器占用的资源,一旦确定不再使用播放器时应当尽早调用它释放资源。
reset()可以使播放器从Error状态中恢复过来,重新会到Idle状态。

4)设置播放器的监听器:
MediaPlayer提供了一些设置不同监听器的方法来更好地对播放器的工作状态进行监听,以期及时处理各种情况,
如: setOnCompletionListener(MediaPlayer.OnCompletionListener listener)、
setOnErrorListener(MediaPlayer.OnErrorListener listener)等,设置播放器时需要考虑到播放器可能出现的情况设置好监听和处理逻辑,以保持播放器的健壮性。

SurfaceView

SurfaceView是视图(View)的继承类,这个视图里内嵌了一个专门用于绘制的Surface。你可以控制这个Surface的格式和尺寸。Surfaceview控制这个Surface的绘制位置。(Surface是原始图像缓冲区(raw buffer)的一个句柄,而原始图像缓冲区是由屏幕图像合成器(screen compositor)管理的)

你可以通过SurfaceHolder接口访问这个surface,getHolder()方法可以得到这个接口。

surfaceview的核心在于提供了两个线程:UI线程和渲染线程。这里应注意:
1> 所有SurfaceView和SurfaceHolder.Callback的方法都应该在UI线程里调用,一般来说就是应用程序主线程。渲染线程所要访问的各种变量应该作同步处理。

2> 由于surface可能被销毁,它只在SurfaceHolder.Callback.surfaceCreated()和 SurfaceHolder.Callback.surfaceDestroyed()之间有效,所以要确保渲染线程访问的是合法有效的surface。

关于SurfaceView我们要知道和注意以下几点:

(1)SurfaceView 允许我们在非UI主线程中改变SurfaceView的内容,一般View是不允许在线程中访问的。由于这个特点游戏开发在界面处理上大多会选择SurfaceView。

(2)在使用SurfaceView的时候一定要等创建成功以后在使用,也就是在SurfaceView的getHolder中添加回调addCallback中的surfaceCreated。

(3)SurfaceView可能被销毁,而且有可能在Activity的OnDestroy()之前销毁,所以在开发中一定要做处理。

(4)SurfaceView位于UI的最底层的视图层次中,允许在其上面添加一下图层,但不能是透明的。

(5)SurfaceView的构成比其它的View复杂,占用资源也多,所以除了使用view不能实现的,再建议使用。

下面是一个例子:

public class TextActivity extends AppCompatActivity {

    private SurfaceView sfv;//能够播放图像的控件
    private SeekBar sb;//进度条
    private String path;//本地文件路径
    private SurfaceHolder holder;
    private MediaPlayer player;//媒体播放器
    private Button Play;//播放按钮
    private Timer timer;//定时器
    private TimerTask task;//定时器任务
    private int position = 0;
    private EditText et;

    int ipx = 0;
    //视频文件需要放在res下新建的raw文件下
    int[] resId = {R.raw.video_1, R.raw.video_2, R.raw.video_3, R.raw.video_4};


    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_text);

        initView();
    }

    //初始化控件,并且为进度条和图像控件添加监听
    private void initView() {
        sfv = (SurfaceView) findViewById(R.id.sfv);
        sb = (SeekBar) findViewById(R.id.sb);
        Play = (Button) findViewById(R.id.play);
        et = (EditText) findViewById(R.id.et);
        Play.setEnabled(false);

        holder = sfv.getHolder();
        holder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);

        sb.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {
            @Override
            public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {

            }

            @Override
            public void onStartTrackingTouch(SeekBar seekBar) {

            }

            @Override
            public void onStopTrackingTouch(SeekBar seekBar) {
                //当进度条停止拖动的时候,把媒体播放器的进度跳转到进度条对应的进度
                if (player != null) {
                    player.seekTo(seekBar.getProgress());
                }
            }
        });

        holder.addCallback(new SurfaceHolder.Callback() {
            @Override
            public void surfaceCreated(SurfaceHolder holder) {
                //为了避免图像控件还没有创建成功,用户就开始播放视频,造成程序异常,所以在创建成功后才使播放按钮可点击
                Log.d("TAG", "surfaceCreated");
                Play.setEnabled(true);
            }

            @Override
            public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
                Log.d("TAG", "surfaceChanged");
            }

            @Override
            public void surfaceDestroyed(SurfaceHolder holder) {
                //当程序没有退出,但不在前台运行时,因为surfaceview很耗费空间,所以会自动销毁,
                // 这样就会出现当你再次点击进程序的时候点击播放按钮,声音继续播放,却没有图像
                //为了避免这种不友好的问题,简单的解决方式就是只要surfaceview销毁,我就把媒体播放器等
                //都销毁掉,这样每次进来都会重新播放,当然更好的做法是在这里再记录一下当前的播放位置,
                //每次点击进来的时候把位置赋给媒体播放器,很简单加个全局变量就行了。
                Log.d("TAG", "surfaceDestroyed");
                if (player != null) {
                    position = player.getCurrentPosition();
                    stop();
                }
            }
        });

    }

    private void play() {

        Play.setEnabled(false);//在播放时不允许再点击播放按钮

        if (isPause) {//如果是暂停状态下播放,直接start
            isPause = false;
            player.start();
            return;
        }



        try {

            player = MediaPlayer.create(this, resId[ipx % 4]);

            player.setDisplay(holder);//将影像播放控件与媒体播放控件关联起来
            // player.setLooping(true); //设置单个循环播放

            player.setOnCompletionListener(new MediaPlayer.OnCompletionListener() {
                @Override
                public void onCompletion(MediaPlayer mp) {//视频播放完成后,释放资源
                    Play.setEnabled(true);

                    ipx++;
                    mp.reset();
                    play();
                    //循环播放下一个
                    mp = MediaPlayer.create(getBaseContext(), resId[ipx % 4]);
                    mp.setDisplay(holder);
                    mp.start();

                }
            });

            player.setOnPreparedListener(new MediaPlayer.OnPreparedListener() {
                @Override
                public void onPrepared(MediaPlayer mp) {
                    //媒体播放器就绪后,设置进度条总长度,开启计时器不断更新进度条,播放视频
                    Log.d("TAG", "onPrepared");
                    sb.setMax(player.getDuration());
                    timer = new Timer();
                    task = new TimerTask() {
                        @Override
                        public void run() {
                            if (player != null) {
                                int time = player.getCurrentPosition();
                                sb.setProgress(time);
                            }
                        }
                    };
                    timer.schedule(task, 0, 500);
                    sb.setProgress(position);
                    mp.seekTo(position);
                    mp.start();

                }
            });

            player.prepareAsync();

        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    public void play(View v) {
        play();
        Log.d("TAG", path);
    }

    private boolean isPause;

    private void pause() {
        if (player != null && player.isPlaying()) {
            player.pause();
            isPause = true;
            Play.setEnabled(true);
        }
    }

    public void pause(View v) {
        pause();
    }

    private void replay() {
        isPause = false;
        if (player != null) {
            stop();
            play();
        }
    }

    public void replay(View v) {
        replay();
    }

    public void back(View v) {
        finish();
        onDestroy();
    }

    private void stop() {
        isPause = false;
        if (player != null) {
            sb.setProgress(0);
            player.stop();
            player.release();
            player = null;
            if (timer != null) {
                timer.cancel();
            }
            Play.setEnabled(true);
        }
    }

    public void stop(View v) {
        stop();
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        stop();
    }


}
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical">

    <EditText
        android:id="@+id/et"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:hint="请输入文件名称例如:aa.mp4,务必确保文件放在sdcard目录下"
        android:visibility="gone" />

    <SurfaceView
        android:id="@+id/sfv"
        android:layout_width="match_parent"
        android:layout_height="0dp"
        android:layout_weight="1" />

    <SeekBar
        android:id="@+id/sb"
        android:layout_width="match_parent"
        android:visibility="gone"
        android:layout_height="wrap_content"
        android:layout_marginTop="10dp"
         />

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="45dp"
        android:background="@android:color/darker_gray"

        android:orientation="horizontal">

        <Button
            android:id="@+id/play"
            android:layout_width="0dp"
            android:layout_height="match_parent"
            android:layout_weight="1"
            android:onClick="play"
            android:background="@android:color/holo_blue_light"
            android:textColor="@android:color/white"
            android:text="播放" />

        <Button
            android:id="@+id/pause"
            android:layout_width="0dp"
            android:layout_height="match_parent"
            android:layout_weight="1"
            android:background="@android:color/holo_blue_light"
            android:textColor="@android:color/white"
            android:onClick="pause"
            android:text="暂停" />

        <Button
            android:layout_width="0dp"
            android:layout_height="match_parent"
            android:layout_weight="1"
            android:onClick="stop"
            android:background="@android:color/holo_blue_light"
            android:textColor="@android:color/white"
            android:text="停止" />

        <Button
            android:layout_width="0dp"
            android:layout_height="match_parent"
            android:layout_weight="1"
            android:background="@android:color/holo_blue_light"
            android:textColor="@android:color/white"
            android:onClick="replay"
            android:text="重播" />

        <Button
            android:layout_width="0dp"
            android:layout_height="match_parent"
            android:layout_weight="1"
            android:background="@android:color/holo_blue_light"
            android:textColor="@android:color/white"
            android:onClick="back"
            android:text="退出" />
    </LinearLayout>

</LinearLayout>

猜你喜欢

转载自blog.csdn.net/Dr_abandon/article/details/80742058