您的位置:首页 > 移动开发 > Android开发

视频播放控件SurfaceView与VideoView

2016-10-05 00:01 253 查看
既然是播放一段视频,那么不可避免的要涉及到一些开始、暂停、停止等操作,

VideoView也为开发人员提供了对应的方法,这里简单介绍一些常用的:

//设置数据源

mediaPlayer.setDataSource(context, Uri.parse(uri) );

 //准备

mediaPlayer.prepareAsync();

//绑定surfaceview

 mediaPlayer.setDisplay(surfaceHolder);

 --------------------------------------------------------

 

int getCurrentPosition():获取当前播放的位置。

int getDuration():获取当前播放视频的总长度。

isPlaying():当前VideoView是否在播放视频。

void pause():暂停

void seekTo(int msec):从第几毫秒开始播放。

void resume():重新播放。

void setVideoPath(String path):以文件路径的方式设置VideoView播放的视频源。

void setVideoURI(Uri uri):以Uri的方式设置VideoView播放的视频源,可以是网络Uri或本地Uri。

void start():开始播放。

void stopPlayback():停止播放。

setMediaController(MediaController controller):设置MediaController控制器。

setOnCompletionListener(MediaPlayer.onCompletionListener l):监听播放完成的事件。

setOnErrorListener(MediaPlayer.OnErrorListener l):监听播放发生错误时候的事件。

setOnPreparedListener(MediaPlayer.OnPreparedListener l)::监听视频装载完成的事件。

  上面的一些方法通过方法名就可以了解用途。和MediaPlayer配合SurfaceView播放视频不同

,VideoView播放之前无需编码装载视频,它会在start()开始播放的时候自动装载视频。

并且VideoView在使用完之后,无需编码回收资源。

使用VideoView做播放器控件的时候

protected void onCreate(Bundle savedInstanceState) {

        super.onCreate(savedInstanceState);

        setContentView(R.layout.activity_main);

        //给videoView填充数据

        //获得videoView对象

        videoViewTest = (VideoView) findViewById(R.id.vv_test);

        //填充网络数控

        videoViewTest.setVideoPath(url); //-参数是视频的网址

        //设置媒体控制器

        videoViewTest.setMediaController(new MediaController(this));

        videoViewTest.setOnPreparedListener(new MediaPlayer.OnPreparedListener() {

            @Override

            public void onPrepared(MediaPlayer mp) {

                mp.start();

            }

        });

    }

    protected void onDestroy() {

        if(videoViewTest != null){

            //释放资源---在super之前调用的原因防止退出报错

            videoViewTest.stopPlayback();

            videoViewTest = null;

        }

        super.onDestroy();

    }

-----=================================================================================

protected void onCreate(Bundle savedInstanceState) {  

        super.onCreate(savedInstanceState);  

        setContentView(R.layout.activity_main);  

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

        mediaco=new MediaController(this);  

        File file=new File("/mnt/sdcard/通话录音/1.mp4");  

        if(file.exists()){  

            //VideoView与MediaController进行关联  

            video1.setVideoPath(file.getAbsolutePath());  

            video1.setMediaController(mediaco);  

            mediaco.setMediaPlayer(video1);  

            //让VideiView获取焦点  

            video1.requestFocus();  

        }  

==========================================================

提到VideoView不得不再说一些MediaController。

虽然VideoView为我们提供了方便的API用于播放、暂停、停止等操作,但是还是需要我们编码完成,

但是如果使用了MediaController的话,那么这些操作都可以省去。

 

  MediaController可以用于配合VideoView播放一段视频,

它为VideoView提供一个悬浮的操作栏,在操作栏中可 以对VideoView播放的视频进行控制,

默认情况下,会悬浮显示三秒。它通过MediaController.setMediaPlayer()方法

 进行指定需要控制的VideoView,但是仅仅这样是不够的,MediaController的控制需要类似于双向控 制,

 MediaController指定控制的VideoView,VideoView还需要指定那个MediaController来控制它,

 这需要使 用VideoView.setMediaController()方法。

 

  下面介绍一下MediaController的一些常用方法;

 

boolean isShowing():当前悬浮控制栏是否显示。

void setMediaPlayer(MediaController.MediaPlayerControl player):设置控制的组件。

void setPrevNextListeners(View.OnClickListener next,View.OnClickListener prev):设置上一个视频、下一个视频的切换事件。

  通过上面的方法可以看出setMediaPlayer()并非指定的是一个VideoView,

而是一个 MediaPlayerControl接口,MediaPlayerControl接口内部定义了一些播放相关的播放、暂停、停止等

操作,而 VideoView实现了MediaPlayerControl。

 

  默认情况下,如果不通过setPrevNextListeners()设置切换视频的监听器,

MediaController是不会显示这两个按钮的。

====================================================================

最近在做一期有关于视频的项目开发。因为项目周期比较紧,而且自己以前也没有对于视频相关的经验积累。所以在开发的时候遇到了问题,自己尝试解决并记录下来。(PS:视频都是下载完再进行播放类似于微信朋友圈的视频功能,播放组件用的是分装的原生的VideoView)

一、视频播放时会有短暂的黑屏时间:

产生原因:视频文件加载到内存中是需要时间,这个时间可能导入VideoView全黑。

解决方法(1):给VideoView添加MediaPlayer.OnPreparedListener 监听事件,在其onPrepared(MediaPlayer mp) 方法回调中播放视频这个时候视频已经完成了加载。(PS:此方法在有些情况下使用有些问题,因为播放视频的时候MediaPlayer.OnPreparedListener 监听不到回调,具体为什么自己也没有做相关的调研,如果有谁知道希望能分享一下)。

解决方法(2):笨方法,同时也是最管用的方法。在VideoView执行start() 方法时视频的预览图不是立即消失而是延迟几百毫秒之后消失,这是视频已经加载完成,当然体验上有些不好。

二、在进入有VideoView界面的Activity时会出现闪黑屏的情况(如论视频是否播放):

产生原因:不祥(自己没有找到认为正确的答案)

解决方法:在整个界面创建之前添加这行代码getWindow().setFormat(PixelFormat.TRANSLUCENT);

三、当前界面有视频播放时进入其他界面,然后跳转回来后VideoView展示全黑(按home键再次进入app也会有同样的问题):

产生原因:VideoView被回收掉,而自己没做VideoView的状态保存处理

解决方法:在VideoView所在的Activity或者Fragment的生命周期中处理VideoView视频播放和暂停。

四、在类似微信列表页视频播放点击大图播放时列表的VideoView回出现在大图的VideoView之上(PS:如果列表的VideoView和大图播放的VideoView不是同一个):

产生原因:SurfaceView默认会出现在最顶部的。

解决方法: 小图播放时要隐藏掉(GONE而不是INVISIBLE)。

五、VideoView嵌套ViewPager使用时,在滑动ViewPager过程中视VideoView会出现透明(此时VideoView是自动播放)(PS:此时Activity的主题为Android:theme="@style/Transparent")。

产生原因:ViewPager在执行public void onPageSelected(int position) 方法时当前界面还是展示两个View。即下一个View并没有完全漏出来。

解决方法:定义ViewPager的ViewPager.OnPageChangeListener接口,覆盖public void onPageScrolled(int postion, float v, int i)方法,监听让下一个界面完全展现出来之后在执行public void onPageSelected(int position)方法。然后再在方法里处理下一个视频的播放以及上一个的停止。

六、使用VideoView造成界面其他组价焦点产生“遗失”:

现象:在ViewTreeObserver.addOnPreDrawListener添加新的OnPreDrawListener的onPreDraw多次执行。

解决办法(1):使用SurfaceView+MediaPlayer自定义播放器

解决办法(2):对VideoView的构造函数中的焦点处理的方法进行反处理,即在VideoView子类(这里应该就是自己写的自定义VideoView其继承与VideoView)的构造方法中进行反处理。

//VideoView源码

public class VideoView extends SurfaceView

        implements MediaPlayerControl, SubtitleController.Anchor {

    private String TAG = "VideoView";

    /*其他代码省略*/

    //每个构造函数中都执行initVideoView

    public VideoView(Context context) {

        super(context);

        initVideoView();

    }

    public VideoView(Context context, AttributeSet attrs) {

        this(context, attrs, 0);

        initVideoView();

    }

    public VideoView(Context context, AttributeSet attrs, int defStyleAttr) {

        this(context, attrs, defStyleAttr, 0);

    }

    public VideoView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {

        super(context, attrs, defStyleAttr, defStyleRes);

        initVideoView();

    }

    /*其他代码省略*/

    private void initVideoView() {

        mVideoWidth = 0;

        mVideoHeight = 0;

        getHolder().addCallback(mSHCallback);

        getHolder().setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);

        //start     下边3行代码是对焦点进行的处理,以及自己所谓的反处理

        setFocusable(true);  //子类setFocusable(false);

        setFocusableInTouchMode(true);   //子类setFocusableInTouchMode(false);

        requestFocus();   //子类clearFocus();

        //end

        mPendingSubtitleTracks = new Vector<Pair<InputStream, MediaFormat>>();

        mCurrentState = STATE_IDLE;

        mTargetState  = STATE_IDLE;

    }

    /*其他代码省略*/

 }

七、在VideoView的MediaPlayer.OnCompletionListener 回调监听public void onCompletion(MediaPlayer mp) 方法里进行视频的播放(PS:这个指的是视频的循环播放),在有些手机上不能正常重新播放。

产生原因:在部分手机上VideoView的MediaPlayer.OnCompletionListener 回调监听public void onCompletion(MediaPlayer mp) 方法里此时`VideoView.isPlaying() 的值还是为true。

解决办法:在VideoView的 MediaPlayer.OnCompletionListener 回调监听 public void onCompletion(MediaPlayer mp) 方法里对`VideoView.start() 方法进行延迟播放。一般这个时间不会太长,几百毫秒就可以。视觉感官上也还好。

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

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:orientation="vertical" android:layout_width="fill_parent"

    android:layout_height="fill_parent" android:weightSum="1">

    <SurfaceView android:layout_height="220dip" android:layout_gravity="center" android:id="@+id/surface" android:layout_weight="0.25" android:layout_width="320dip"></SurfaceView>

    <LinearLayout android:id="@+id/linearLayout1" android:layout_height="wrap_content" android:layout_width="fill_parent">

        <Button android:text="播放" android:id="@+id/button1" android:layout_width="wrap_content" android:layout_height="wrap_content"></Button>

        <Button android:text="暂停" android:id="@+id/button2" android:layout_width="wrap_content" android:layout_height="wrap_content"></Button>

        <Button android:text="停止" android:id="@+id/button3" android:layout_width="wrap_content" android:layout_height="wrap_content"></Button>

    </LinearLayout>

</LinearLayout>

public class SurfaceActivity extends Activity implements SurfaceHolder.Callback {

    /** Called when the activity is first created. */

    MediaPlayer player;

    SurfaceView surface;

    SurfaceHolder surfaceHolder;

    Button play,pause,stop;

    @Override

    public void onCreate(Bundle savedInstanceState) {

        super.onCreate(savedInstanceState);

        setContentView(R.layout.main);

        play=(Button)findViewById(R.id.button1);

        pause=(Button)findViewById(R.id.button2);

        stop=(Button)findViewById(R.id.button3);

        surface=(SurfaceView)findViewById(R.id.surface);

 

        surfaceHolder=surface.getHolder();  //SurfaceHolder是SurfaceView的控制接口

        surfaceHolder.addCallback(this);   //因为这个类实现了SurfaceHolder.Callback接口,所以回调参数直接this

        surfaceHolder.setFixedSize(320, 220);  //显示的分辨率,不设置为视频默认

        surfaceHolder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);  //Surface类型

 

        play.setOnClickListener(new OnClickListener(){

             @Override

            public void onClick(View v) {

                player.start();

            }});

        pause.setOnClickListener(new OnClickListener(){

            @Override

            public void onClick(View v) {

                player.pause();

            }});

        stop.setOnClickListener(new OnClickListener(){

             @Override

            public void onClick(View v) {

                player.stop();

            }});

    }

 

    @Override

    public void surfaceChanged(SurfaceHolder arg0, int arg1, int arg2, int arg3) {

    }

    @Override

    public void surfaceCreated(SurfaceHolder arg0) {
//必须在surface创建后才能初始化MediaPlayer,否则不会显示图像

        player=new MediaPlayer();

        player.setAudioStreamType(AudioManager.STREAM_MUSIC);

        player.setDisplay(surfaceHolder);

        //设置显示视频显示在SurfaceView上

            try {

                player.setDataSource("/sdcard/3.mp4");

                player.prepare();

            } catch (Exception e) {

                e.printStackTrace();

            }

    }

    @Override

    public void surfaceDestroyed(SurfaceHolder arg0) {

        // TODO Auto-generated method stub

 

    }

 

@Override

    protected void onDestroy() {

        // TODO Auto-generated method stub

        super.onDestroy();

        if(player.isPlaying()){

        player.stop();

        }

        player.release();

        //Activity销毁时停止播放,释放资源。不做这个操作,即使退出还是能听到视频播放的声音

    }

}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  Android基础 视频