视频播放控件SurfaceView与VideoView

来源:互联网 发布:移动网络运营商是哪个 编辑:程序博客网 时间:2024/06/11 08:35
既然是播放一段视频,那么不可避免的要涉及到一些开始、暂停、停止等操作,
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销毁时停止播放,释放资源。不做这个操作,即使退出还是能听到视频播放的声音
    }
}
0 0
原创粉丝点击