发现一般的UI界面都是显示在Unity界面的后面的,即Unity控件的zOrder是最高的。
用于WebRtc显示的SurfaceViewRender在视频(本地或者远程)出来后是能显示到Unity上面的,但是视频还没有出来时,等待时,是被Unity遮挡着的。
查到一个资料:https://android.jlelse.eu/unity-and-android-connecting-the-dots-6368b31e86c5,这个网页在家里电脑居然显示不了,公司电脑可以(有科学上网)。
英文的,他们是要在unity上面显示其他东西。
这个效果是我需要的。
调试过程中需要查看程序界面结构,记得AndroidStudio支持的。以前是用DeviceMonitor(参考:Android studio 查看页面布局层次结构工具使用教程),现在(AndroidStudio3.5.2)有新的工具(参考:AndroidStudio3.0以上替代DDMS的hierarchyviewer工具查看布局层级)。
从上面的资料以及LayoutInspector来看,UnityPlayer里面确实只有一个SurfaceView。
资料的内容总之就是
1.UnityPlayer里面设置了ZOrder,setZOrderOnTop,所有是显示在最前面的。
public UnityPlayer(ContextWrapper contextWrapper) {
super(contextWrapper);
...
this.mSurfaceView = new SurfaceView((Context)contextWrapper);
this.mSurfaceView.getHolder().setFormat(2);
...
this.mSurfaceView.setFocusable(true);
this.mSurfaceView.setFocusableInTouchMode(true);
// added on Unity 5.6
// removed in patch 5.6.1p4
this.mSurfaceView.setZOrderOnTop(true);
addView(mSurfaceView);
}
我看到的代码是这样的:
private SurfaceView c() {
SurfaceView var1 = new SurfaceView(this.t);
if (b()) {
var1.getHolder().setFormat(-3);
var1.setZOrderOnTop(true);
} else {
var1.getHolder().setFormat(-1);
}
var1.getHolder().addCallback(new Callback() {
....
});
var1.setFocusable(true);
var1.setFocusableInTouchMode(true);
return var1;
}
想起来js代码格式化后的,这也是格式化了的,c是什么b是什么t是什么....
想着是不是能够写一个MyUnityPlayer继承或者复制UnityPlayer的内容后修改。继承的话没有找到相关的可修改的接口,复制的话,看不懂,而且语法上过不去。不像以前干过的还原awrtc.js的过程,至少它的语法是没问题的,慢慢里面内容修改名称就好了。
2.可以通过修改SurfaceView的ZOrder的方式来处理,但是会有副作用,黑屏一下。
不知道他的具体代码是怎样的,我的要么Unity部分直接就是变黑了,要么Acitivity退出了。根据在不同地方调用下面的代码。
private void setSurfaceViewZ(){
SurfaceView unitySurfaceView=(SurfaceView)mUnityPlayer.getChildAt(0);
mUnityPlayer.removeView(unitySurfaceView);
unitySurfaceView.setZOrderOnTop(false);
mUnityPlayer.addView(unitySurfaceView);
//mUnityPlayer.resume();
}
在AS里面查看UnityPlayer的代码,发现是处理过的。
3.可以通过创建并显示一个透明的PopupWindow的方式,实现在Unity上面显示窗口的效果。
把资料里面代码直接拷贝过来会发现,缺少setPopUpWindowLayoutType。
这个TYPE_APPLICATION_SUB_PANEL是核心:
总之就是PopupWindow的默认属性是TYPE_APPLICATION_PANEL,要改成TYPE_APPLICATION_SUB_PANEL,Unity的默认也是TYPE_APPLICATION_PANEL。TYPE_APPLICATION_SUB_PANEL会显示在TYPE_APPLICATION_PANEL的前面。
百度上找不到什么setPopUpWindowLayoutType()的有用的信息,goolge上能够搜索到github里面的代码,其实像这样找不到代码时直接在github上找,一般能找到相关的线索。
这两个都是UnityAndroid相关的项目,有空再研究研究。先把需要的代码拿出来,参考1里的
public static void setPopUpWindowLayoutType(PopupWindow popupWindow, int layoutType) {
try {
Method method = PopupWindow.class.getDeclaredMethod("setWindowLayoutType", int.class);
method.setAccessible(true);
method.invoke(popupWindow, layoutType);
} catch (NoSuchMethodException exception) {
Log.w(TAG, String.format("Unable to set popUpWindow window layout type: %s",
exception.getLocalizedMessage()));
} catch (IllegalAccessException exception) {
Log.w(TAG, String.format("Unable to set popUpWindow window layout type: %s",
exception.getLocalizedMessage()));
} catch (InvocationTargetException exception) {
Log.d(TAG, String.format("Unable to set popUpWindow window layout type: %s",
exception.getLocalizedMessage()));
}
}
感觉是用反射的方式修改属性,大概没有直接开放接口吧。(是不是也能继承UnityPlayer并用反射的方式修改abcd呢...)
两个代码都加入项目中,(点击按钮)创建PopupWindow,看不到效果,因为是透明的,relativeLayout弄个背景色,就能看到一个挡住全部区域的界面,包括Unity也被挡住了。
把要显示的SurfaceViewRender放到里面,可以显示。但是问题是挡住了,也就意味着无法触发Unity的事件了。我最开始不打算用Dialog也是因为这个问题。
换个思路,修改PopupWindow的大小,不全屏,拖动PopupWindow就好了。
参考:5分钟搞定PopUpWindow,Android Popupwindow 拖动
过程就不写了,总之这个思路可以,最后得到个UnityPopupWinodw的类。
-----------------------------------------------------------------------------------------------------------
经过测试,公司的大部分手机(Android9,Android10)都得到预期的效果了,但是碰到一个Android7的手机,发现很奇怪的情况。
我的实际项目应用是显示远程摄像机的监控视频,用WebRtc,参考:视频服务器(11) Kurento[6] Android播放。
1.SurfaceView在PopupWindow中会形成显示不出来,同时形成一个洞,可以看到Window后面的界面。
界面:
运行后,这两个地方就“开洞”了:
2.SurfaceViewRenderer(本质是SurfaceView)在视频播放后,不会自动显示到Unity层上面,还是被Unity挡住了。
第一种情况,没办法,考虑放弃用PopupWIndow。
第二种情况,就是图层的问题了,还是ZOrder。
继续重新看一遍前面的资料,https://android.jlelse.eu/unity-and-android-connecting-the-dots-6368b31e86c5,
疑惑1:到底有没有setZOrderOnTop?
正好前面接触了一下java的反射,而我的C#知识告诉我可以用这种方式访问private的方法的。
所以用反射的方式在合适的地方,调用b(),看一下结果。
Method method = UnityPlayer.class.getDeclaredMethod("b");
method.setAccessible(true);
Object result=method.invoke(null);
结果是false。
看来一下UnityPlayer的构造函数
UnityPlayer本身就是一个FrameLayout里面加一个SurfaceView,所有的三维内容都是绘制在SurfaceView里面的。
所以方案就是将显示视频的SurfaceViewRender也放到这个FrameLayout并显示在Unity的SurfaceView的上面。
----------------------------------------------------------
视频界面显示出来了,还有个问题是,位置不正确。设置为(0,0)的时候看不到,用Tools->Layout Inspector查看了一下,方向位置偏到手机屏幕的上方去了,比如(0,-1433)的位置。
发现有两种情况,UnityPlayer也是放在一个LinearLayout里面显示的,SurfaceViewRender假如这个LinnerLayout的layout_height是match_parent,则会有这个问题,加入layout_height是一个具体高度,比如500dp,则没有这个问题,设置(0,0)的情况下,就是在左上角。
手机横屏竖屏旋转时也有这个问题,在一个角度下正常显示的界面,到另一个角度就不见了。
考虑和获取控件宽度的问题一样,需要在重新布局后再去获取宽度,设置位置。
结论上讲就是在初始化UnityPlayer的地方添加addOnGlobalLayoutListener,在里面修改位置信息,需要修改的目标坐标已经保存变量里了。