学习的第一步就是先找到函数的入口:根据R.id.fullscreen这个全屏按钮的点击事件,我们很快就能定位到名叫gotoScreenFullscreen的函数。
code line 737
public void gotoScreenFullscreen() {
ViewGroup vg = (ViewGroup) getParent();
vg.removeView(this);
cloneAJzvd(vg);
CONTAINER_LIST.add(vg);//作者在这里保存了原视图的父布局
vg = (ViewGroup) (JZUtils.scanForActivity(getContext())).getWindow().getDecorView();//和他也没有关系
vg.addView(this, new FrameLayout.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT));
setScreenFullscreen();
JZUtils.hideStatusBar(getContext());
JZUtils.setRequestedOrientation(getContext(), FULLSCREEN_ORIENTATION);
JZUtils.hideSystemUI(getContext());//华为手机和有虚拟键的手机全屏时可隐藏虚拟键 issue:1326
}
从上方代码第三行跳转到下面这个方法,可以看出作者用构造器构造了一个新的对象替代了原来位置的对象。
public void cloneAJzvd(ViewGroup vg) {
try {
Constructor<Jzvd> constructor = (Constructor<Jzvd>) Jzvd.this.getClass().getConstructor(Context.class);
Jzvd jzvd = constructor.newInstance(getContext());
jzvd.setId(getId());
vg.addView(jzvd);
jzvd.setUp(jzDataSource.cloneMe(), SCREEN_NORMAL, mediaInterfaceClass);
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InstantiationException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
} catch (NoSuchMethodException e) {
e.printStackTrace();
}
}
继续看第一个方法第四行开始,毫无疑问,作者获取getDecorView()根视图,然后将原来的对象放入了跟视图,同时隐藏了状态栏,也即是开启了全屏模式,汗,简单粗暴。
还不够,那如何从全屏模式回来呢,作者在打开全屏的时候,就将原视图的父容器存放到了一个链表中。
public static boolean backPress() {
Log.i(TAG, "backPress");
if (CONTAINER_LIST.size() != 0 && CURRENT_JZVD != null) {//判断条件,因为当前所有goBack都是回到普通窗口
CURRENT_JZVD.gotoScreenNormal();
return true;
} else if (CONTAINER_LIST.size() == 0 && CURRENT_JZVD != null && CURRENT_JZVD.screen != SCREEN_NORMAL) {//退出直接进入的全屏
CURRENT_JZVD.clearFloatScreen();
return true;
}
return false;
}
进入正题
public void gotoScreenNormal() {//goback本质上是goto
gobakFullscreenTime = System.currentTimeMillis();//退出全屏
ViewGroup vg = (ViewGroup) (JZUtils.scanForActivity(getContext())).getWindow().getDecorView();
vg.removeView(this);
CONTAINER_LIST.getLast().removeAllViews();
CONTAINER_LIST.getLast().addView(this, new FrameLayout.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT));
CONTAINER_LIST.pop();
setScreenNormal();//这块可以放到jzvd中
JZUtils.showStatusBar(getContext());
JZUtils.setRequestedOrientation(getContext(), NORMAL_ORIENTATION);
JZUtils.showSystemUI(getContext());
}
看完上面的代码一开始有点蒙蔽,为啥要把父容器的中视图全清理掉。回忆之前的接入过程,是因为
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent">
<cn.jzvd.JzvdStd
android:id="@+id/jz_video"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</LinearLayout>
如上所示,作者一开始就要求为播放器增加一层嵌套,可能是为了图方便,毕竟这比从复杂布局中恢复容易的多。
至此了解饺子播放器全屏播放的原理了。
感想:我也想过做全屏播放,可从没想过这样来切换,我想的是构造新的SufaceView然后重新接入视屏播放的数据,把问题想复杂了,感谢大佬的代码让我豁然开朗。