ListView 列表播放视频,支持横竖屏无缝切换

http://blog.csdn.net/xiaoxin_android/article/details/51116140

最近在做项目的时候有需求要在 listView 中播放视频,并且支持横竖屏无缝切换,在网上搜索了一下,关于这种 demo真的很少, 有的也只是实现简单的功能,无法满足项目中的需求,想着修改一下凑合用,但是各种bug, 毕竟不是自己写的,后来干脆就自己写一个.

由于视频录制出现问题,效果图就不贴了,说一下这个 demo的实现的效果吧, listView中视频播放,可以控制播放与暂停,支持横竖屏无缝切换,切换时是在同一个 activity 中执行的.我发现网上很多 demo 在做切换的时候都是将 mediaPlayer写成单例模式,切换到横屏时跳转到新的 activity,我觉得这样不是很好.

要实现这样一个效果,我们需要分布来解决几个问题 
1.视频播放,支持 listView上下滑动 
2.需要控制界面控制播放暂停 
3.支持横竖屏切换 
视频播放在demo 中是通过 mediaPlayer + textureView 来实现的: 
mediaPlayer 容易出现的问题就是它的状态,它的内部播放机制有自己的状态,要是控制不好就会抛异常,官网的一张图片很好的展示了 mediaPlayer 各个状态之间的关系和转换方式: 
这里写图片描述 
在处理这个问题的时候我参考了 VideoView 的源码,效果还是不错的,看代码

private boolean isPlayState() {
        return (mediaPlayer != null &&
                mCurrentState != STATE_ERROR &&
                mCurrentState != STATE_END &&
                mCurrentState != STATE_PREPARING);
    }
   
   
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

在调用start(),pause()等方法的时候判断一下是不是为isPlayState() 是不是为 true 就可以.在 mediaPlayer的API 中提到当 mediaPlayer处于 preparing状态时调用其他方法不知会有后果位置,所以在这里把这个状态也要刨除. 
播放视频不仅需要 mediaPlayer,好需要通过某种方式将图像输出到屏幕上,否则是能听到声音没有图像,很多视频播放用的是 surfaceview,我这里用的是 textureView, 二者互称姐妹,surfaceview 没有 textureView灵活,放到 listView 中当滚动的时候会出现一些问题,关于二者的优缺点在这里不细说,自行百度.

第三个问题应该是最难得,在同一个 activity中实现屏幕切换.实现过程如下: 
在manifest中设置视频播放的 activity

android:configChanges="orientation|keyboardHidden|screenSize"
   
   
  • 1
  • 1

设置这个的目的是截获屏幕切换事件,当屏切换时会执行 activity的onConfigurationChanged方法,如果不这麽做,在屏幕切换时 activity会从新走一遍它的生命周期,我们的数据就会丢失,当然了也可以通过方法来报错数据,不到说,看看onConfigurationChanged方法

@Override
    public void onConfigurationChanged(Configuration newConfig) {
        super.onConfigurationChanged(newConfig);
        if (playView != null && newConfig.orientation == Configuration.ORIENTATION_LANDSCAPE) {

           // scrollDistance = currentItemView.getTop();

            //获取状态栏高度(如果设置沉浸式状态栏了就不需要获取)
            Rect rect = new Rect();
            getWindow().getDecorView().getWindowVisibleDisplayFrame(rect);
            currentItemView.setLayoutParams(new AbsListView.
                    LayoutParams(ListView.LayoutParams.MATCH_PARENT,
                    getWindowManager().getDefaultDisplay().getHeight() - rect.top));
            //设置横屏后要显示的当前的 itemView
            videoList.post(new Runnable() {
                @Override
                public void run() {
                    //一定要对添加这句话,否则无效,因为界面初始化完成后 listView 失去了焦点
                    videoList.requestFocusFromTouch();
                    videoList.setSelection(currentPosition);
                }
            });
            Log.i("XX", "横屏");
        } else if (playView != null && newConfig.orientation == Configuration.ORIENTATION_PORTRAIT) {
            //横屏时的设置会影响返回竖屏后的效果, 这里设置高度与 xml 文件中的高度相同
            Log.i("MM", currentPosition + "竖屏");
            currentItemView.setLayoutParams(new AbsListView.LayoutParams(
                    ListView.LayoutParams.MATCH_PARENT, getResources()
                    .getDimensionPixelOffset(R.dimen.itmes_height)));
            //本来想切换到竖屏后恢复到初始位置,但是上部出现空白
//            videoList.scrollBy(0, -(scrollDistance));
            //通过该方法恢复位置,不过还是有点小问题
            videoList.post(new Runnable() {
                @Override
                public void run() {
                    videoList.requestFocusFromTouch();
                    videoList.setSelection(firstVisiblePosition);
                }
            });
            Log.i("XX", "竖屏");
        }
    }
   
   
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42

代码中注释写得很详细,简单说一下思路,当屏幕旋转到横屏时,动态设置 listView 的itemView 的宽高充满屏幕,但是有个问题,如果播放的 itemView 竖屏时显示在底部,那么我们切换到横屏时就显示不全,甚至在可视界面之外,这之后就需要想办法可是界面显示的是正在播放视频的 itemView,方法如下:

videoList.setSelection(firstVisiblePosition);
   
   
  • 1
  • 1

这是 listView的方法,但是运行以后不起作用,因为 界面在初始化时 listView 失去了焦点,要重新获得焦点,在前面加上一句话:

videoList.requestFocusFromTouch();
   
   
  • 1
  • 1

并且要通过异步的方式完成. 
显示的问题解决了,但是后又一个问题,因为视频释放到 listView 中的,当屏幕切换到横屏时,它也会上下滑动,所以在屏幕切换到横屏时我们要禁止 listView滑动,这是就要在 activity 的事件分发中做手脚了:

@Override
    public boolean dispatchTouchEvent(MotionEvent ev) {
        switch (ev.getAction()) {
            case MotionEvent.ACTION_MOVE:
                if (getResources().getConfiguration().orientation == Configuration.ORIENTATION_LANDSCAPE) {
                    return true;
                }
                break;
        }
        return super.dispatchTouchEvent(ev);
    }
   
   
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11

横屏时截获滑动事件,消费掉即可. 
当处于横屏时点击 home 键会直接退出当前activity, 所以要早onKeyDown方法中判断一下,如果是横屏点击返回键就让它退出横屏:

@Override
    public boolean onKeyDown(int keyCode, KeyEvent event) {
        if (keyCode == KeyEvent.KEYCODE_BACK) {
            if (getResources().getConfiguration().orientation == Configuration.ORIENTATION_LANDSCAPE) {
                setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
                if (playView != null) {
                    playView.setExpendBtn(false);
                }
                return true;
            }
        }
        return super.onKeyDown(keyCode, event);
    }
   
   
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13

点击横屏后再返回竖屏,由于是在同一个 activity 中实现的,横屏时的操作会影响返回竖屏时的界面效果,所以在竖屏时也要动态的设置下 itemView 的宽高,我在 xml文件中设置 itemView 的宽高时200dp,所以这里也设置下宽高为200dp.

到此为止我们所要实现的功能基本都实现了, demo 中有详细的注释 
代码下载地址: http://download.csdn.net/detail/xiaoxin_android/9487956

该 demo 现在也有点问题,当我点击 home 键进入后台时再返回,播放器会重新播放视频,报错如下: 
E/libEGL: validate_display:245 error 3008 (EGL_BAD_DISPLAY) 
E/SurfaceTexture: unnamed-8128-0 error creating EGLImage: 0x3008 
E/SurfaceTexture: [Android::status_t android::SurfaceTexture::convertToAuxSlotLocked(bool)] create aux eglImage FAILED 
差了很多资料也没找到解决办法,各位看客如果有什么好的解决办法,请留言,不胜感激!!

2
0
 
 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值