Android JieCaoVideoPlayer 全屏实现分析

本来计划写一篇 JieCaoVieoPlayer 源码分析,但是最近项目紧,时间不太够。今天一位朋友问了一个问题,大概就是实现全屏,虽然她不是视频全屏,但原理差不多。所以就先分析下全屏的实现。

在没有看 JieCaoVieoPlayer 之前,自己的 APP 也是需要实现类似微博、qq、今日头条 播放视频的时候,点击全屏,可以炫酷的旋转无间隙的全屏播放,当时怎么做得呢,权当记录:在点击全屏按钮后,先隐藏一切不相关的控件,将视频控件设置为全屏,并且设置旋转效果横屏播放,效果呢,因为仅仅是隐藏控件设置宽高旋转,完全无缝播放,产品要求的效果也达到了;在全屏模式下点击退出全屏按钮,逻辑就是旋转到竖屏设置为原始宽高,显示其他控件,也同样实现了,最后我还是放弃了这种方法,为什么?这种方法对于简单界面能够完美实现,但是当复杂布局情况下,设置显示隐藏的控件太多,最关键的退出全屏就出现了问题。在列表播放视频情况下,退出全屏的时候,返回后界面发生了变动,本来在中间的 item 退出全屏可能在上面,也可能在下面。原因:在全屏的时候,横竖屏切换导致 item 位置发生变动,采用记录位置还是有一些偏差,所以也放弃了这种方法。下面进入今天的正题。

R.id.content

这个东西是今天的关键,关于这个是什么,先看下图

这里写图片描述

自己用画图工具画的,比较难看,先将就下。R.id.content 是什么呢,想知道详解去谷歌下,这里推荐一篇文章,类似的很多 Android应用setContentView与LayoutInflater加载解析机制源码分析 ,认真看了就知道图上面的意思了。这里说最简单的,我们在写 Activity 布局的时候,总需要写 setContentView (R.layout.my_layout),通过调用一系列方法,我们的布局最后显示在上图中的 ContentView ,而且还是一个 FrameLayout ,这个 FrameLayout 控件 id 就是 R.id.content 。其他 View 是什么看看其他文章就清楚了,这里不详解。 也就是说,我们平时为 Activity 写的布局 最后都是添加到 ContentView ,因为 Fragment 是依附在 Activity ,所以Fragment 的布局文件也是添加到 ContentView 。(这里的 DecorView 也是一个 Fragment ,同样可以达到效果)

JieCaoVieoPlayer 的全屏实现

早期的全屏实现和现在有点区别的,我接触的时候方式是:点击全屏的时候,新建一个Activtiy ,将视频地址 videoUrl 和 播放进度 一并传入新的 Activtiy 中,Acitivity 就只有播放控件,全屏播放很方便,但是和无缝播放就有点差距了,毕竟有一个 Activity 的创建需要时间,还有播放控件的添加。后来完善了,大致和现在的实现方式差不多。直接上关键代码。

public void startWindowFullscreen() {

        //隐藏  Activity ActionBar
        hideSupportActionBar(getContext());
        //由竖屏切换为由重力感应决定方向
        JCUtils.getAppCompActivity(getContext()).setRequestedOrientation(FULLSCREEN_ORIENTATION);

        //得到 contentView
        (JCUtils.scanForActivity(getContext())).findViewById(Window.ID_ANDROID_CONTENT);

        //这里的 old 指的是上次向 contentView 添加的 jcVideoPlayer1 ,在退出全屏的时候 
         //contentView 已经移除了 jcVideoPlayer1 ,所以 jcVideoPlayer1 可能为空,假如还在
        // ,就需要移除,为后面 contentView 添加新的 jcVideoPlayer2 让出位置
        View old = vp.findViewById(FULLSCREEN_ID);
        if (old != null) {
            vp.removeView(old);
        }

        //JCMediaManager.mediaPlayer 播放的视频交由 JCMediaManager.textureView 显示
        // 在非全屏界面先移除 JCMediaManager.textureView ,
        //此时视频界面已经看不见了,但是 mediaPlayer 还在运行,所以有声音
        textureViewContainer.removeView(JCMediaManager.textureView);

        try {
            //生成一个新的 jcVideoPlayer 控件
            Constructor<JCVideoPlayer> constructor = (Constructor<JCVideoPlayer>) 
            JCVideoPlayer.this.getClass().getConstructor(Context.class);
            final JCVideoPlayer jcVideoPlayer = constructor.newInstance(getContext());
            //给新的jcVideoPlayer 赋值 id,用于识别这个控件,方便移除
            jcVideoPlayer.setId(FULLSCREEN_ID);
            //设置宽高
            FrameLayout.LayoutParams lp = new FrameLayout.LayoutParams(
                    ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT);
            vp.addView(jcVideoPlayer, lp);
            jcVideoPlayer.setUp(url, JCVideoPlayerStandard.SCREEN_WINDOW_FULLSCREEN, objects);
            jcVideoPlayer.setState(currentState);

            //向新的 jcVideoPlayer 添加 JCMediaManager.textureView ,上面在非全屏界面移除的
            // JCMediaManager.textureView 实际上是没有销毁,
            // 生成新的 jcVideoPlayer 后将   JCMediaManager.textureView 转移到了 新的
            // jcVideoPlayer,因为创建 jcVideoPlayer 速度很快,
            // 所以感觉不到有延迟,如果速度慢,就会感觉到切换不流畅,会出现黑屏等等。

            jcVideoPlayer.addTextureView();


            JCVideoPlayerManager.setSecondFloor(jcVideoPlayer);
            CLICK_QUIT_FULLSCREEN_TIME = System.currentTimeMillis();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

上面的注释很很清楚了,尽量看看,大致流程如下

  • 隐藏系统界面的 ActionBar : 不是我们写的布局上面的 ActionBar;

  • 设置由竖屏切换为重力感应决定方向;

  • 在 contentView 添加 一个 新的 JCVideoPlayer,这里暂且把 非全屏的中 的 JCVideoPlayer 叫做 videoPlayer_1 ,把像 contentView 添加的叫做 videoPlayer_2 。

  • 将 videoPlayer_1 中 TextureView 移除掉,然后 再把 TextureView 添加到 videoPlayer_2 中。

这里注意下,这里的全屏无间隙切换是怎么做到的,而且声音没有停止,画面连续(前提是视频正在播放)。

在进行上面的代码,我们的 mediaplayer 并没有暂停,一直都在进行播放,所以声音是一直存在的,至于画面连续。这个问题我开始有疑问

textureViewContainer.removeView(JCMediaManager.textureView);

我想的是既然 removeView 了,textureView 就应该被销毁了

jcVideoPlayer.addTextureView();

public void addTextureView() {
        Log.d(TAG, "addTextureView [" + this.hashCode() + "] ");
        FrameLayout.LayoutParams layoutParams = new 
        FrameLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, 
        ViewGroup.LayoutParams.MATCH_PARENT,
                Gravity.CENTER);
        textureViewContainer.addView(JCMediaManager.textureView, layoutParams);
    }

后面又向 videoPlayer_2 添加了 textureView ,我就比较有疑问了,这个 textureView 已经被 remove ,为什么又能继续添加呢,关键还能正常播放,为了验证这个问题,进行如下:

 textureViewContainer.removeView(JCMediaManager.textureView);
 if (JCMediaManager.textureView == null) {
     Log.i("test", "textureView == null");
 } else {
     Log.i("test", "textureView != null");
     JCMediaManager.textureView.setTag("Test this textureView");
     Log.i("test", "textureView tag1 = " + JCMediaManager.textureView.getTag());
  }


//在 jcVideoPlayer.addTextureView()方法中也打印 log
public void addTextureView() {
    FrameLayout.LayoutParams layoutParams = new 
    FrameLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, 
    ViewGroup.LayoutParams.MATCH_PARENT,Gravity.CENTER);
    textureViewContainer.addView(JCMediaManager.textureView, layoutParams);

     Log.i("test", "textureView tag2 = " + JCMediaManager.textureView.getTag());
    }

在移除后判断 textureView 是否为空 ,不为空就设置 tag 并打印,addTextureView 后再次打印 tag,结果如下

这里写图片描述

结果是非空,并且前后是同一个 textureView ,得出结论就是 相当于把 textureView 从 videoPlayer_1 拷贝到了 videoPlayer_2 中,因为拷贝时间很快,我测试的时间 是 15ms 左右,拷贝时间慢的话会出现什么情况:

     new Handler().postDelayed(new Runnable() {
             public void run() {
                jcVideoPlayer.addTextureView();
               }
            }, 3000);

这里延迟3秒添加,结果如下

这里写图片描述

很明显,点击全屏后会有一段时间(也就是3s)的黑屏,在 textureView 移除后没有添加到新的 videoPlayer_2 上面,是不能显示视频,但是声音是正常播放的,说明整个过程 mediaplayer 一直都在播放。

小结

全屏的功能主要利用了 contentView 将显示视频的 textureView 换了一个位置,继续播放。利用 contentView 或者 decorView 还可以实现其他类似的功能,只要这个思路就好。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值