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

最近在做项目的时候有需求要在 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);
    }

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

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

android:configChanges="orientation|keyboardHidden|screenSize"

设置这个的目的是截获屏幕切换事件,当屏切换时会执行 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", "竖屏");
        }
    }

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

videoList.setSelection(firstVisiblePosition);

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

videoList.requestFocusFromTouch();

并且要通过异步的方式完成.
显示的问题解决了,但是后又一个问题,因为视频释放到 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);
    }

横屏时截获滑动事件,消费掉即可.
当处于横屏时点击 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);
    }

点击横屏后再返回竖屏,由于是在同一个 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
差了很多资料也没找到解决办法,各位看客如果有什么好的解决办法,请留言,不胜感激!!

  • 3
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 3
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值