先看一下问题
如图所示,在点击课件和商城两个按钮时,本来应该是加载两个不同的网页,但实际效果却不是这样,先添加的课件fragment会遮挡后添加的商城fragment,下面贴上fragment的切换代码
初步判断是在fragment切换时前一个fragment没被隐藏掉,下面开始爬坑之旅
爬坑之旅
首先想到的是查看XWalkView的源码
public XWalkView(Context context) {
super(context, (AttributeSet)null);
SurfaceView surfaceView = new SurfaceView(context);
surfaceView.setLayoutParams(new LayoutParams(0, 0));
this.addView(surfaceView);
this.constructorTypes = new ArrayList();
this.constructorTypes.add(Context.class);
this.constructorParams = new ArrayList();
this.constructorParams.add(context);
this.postWrapperMethod = new ReflectMethod(this, "postXWalkViewInternalContextConstructor", new Class[0]);
this.reflectionInit();
}
public XWalkView(Context context, AttributeSet attrs) {
super(context, attrs);
if (!this.isInEditMode()) {
SurfaceView surfaceView = new SurfaceView(context);
surfaceView.setLayoutParams(new LayoutParams(0, 0));
this.addView(surfaceView);
this.constructorTypes = new ArrayList();
this.constructorTypes.add(Context.class);
this.constructorTypes.add(AttributeSet.class);
this.constructorParams = new ArrayList();
this.constructorParams.add(context);
this.constructorParams.add(attrs);
this.postWrapperMethod = new ReflectMethod(this, "postXWalkViewInternalContextAttributeSetConstructor", new Class[0]);
this.reflectionInit();
}
}
我们看到XWalkView内部添加了一个surfaceview,看到这里,心理一群草泥马翻腾而过,这里再复习一下surfaceview,它拥有独立的绘图表面,即它不与其宿主窗口共享同一个绘图表面。来描述SurfaceView的Layer或者LayerBuffer的Z轴位置是小于用来其宿主Activity窗口的Layer的Z轴位置的,但是前者会在后者的上面挖一个“洞”出来,以便它的UI可以对用户可见。实际上,SurfaceView在其宿主Activity窗口上所挖的“洞”只不过是在其宿主Activity窗口上设置了一块透明区域。到这里再分析一下以上问题,当fragment隐藏时,普通的view所在的绘图表面进行了隐藏,但是surfaceview所在的绘图表面并没有被隐藏掉,导致前一个fragment的surfaceview没有被隐藏掉,从而引发上述问题。
接着爬坑,知道了原理,就先要找到XWalkView中的surfaceview,本以为只要xWalkView.getChildAt(0)
就可以了,谁知一运行立马崩溃了,明明是this.addView(surfaceView)
吗,没办法只能一层一层的去找了,费了半天劲终于找到这货了
val b = xWalkView.getChildAt(0) as XWalkViewBridge
surfaceView = (b.getChildAt(0) as ViewGroup).getChildAt(0) as SurfaceView
wc,这整整包裹了三层,到此爬坑暂停,下面开始解决问题
解决方案
首先还是想在fragment切换时隐藏前一个fragment的surfaceview,在网上搜了半天,得到这个结论:
Surfaceview的特性:内部维护了两个线程即主线程和渲染线程,渲染线程可以在主线程之外的线程中向屏幕上绘图。这样可以避免主线程因绘图任务繁重导致程序的阻塞,从而提高了程序的反应速度。在游戏开发中多用surfaceView,游戏的背景,任务,动作尽量在画布Canvas中绘制。这种双线程的设计模式,极大的消耗了CPU内存,为此,SurfaceView可见时才被创建,SurfaceView隐藏时便被销毁,从而达到节约内存的目的。
SurfaceView可见时才被创建,SurfaceView隐藏时便被销毁,这隐藏跟删除没区别啊,并且在fragment隐藏时surfaceview并不会主动隐藏,需要我们去设置其可见性,所以抛弃fragment的hide|show切换方式,改为replace方式,
这样由系统控制其创建和删除,本以为这样就大功告成了,结果又高兴早了
遮挡问题是解决了,但这么大一块黑屏,不能忍,接着上网搜资料,找到黑屏出现的原因
原因:
SurfaceView因为不同于一般的view,它有自己良好的缓冲以及数据存取机制,系统对他有特殊处理。当surfaceview第一次在当前activity上添加的时候,系统会给WindowManager重新排布局,relayout,这样就会黑一下,这个只会出现在第一次,以后再添加surfaceview时就不会黑屏了。而我们采用的添加删除fragment方式刚好引发这个问题
解决:首先给xWalkView设置背景颜色,然后将surfaceview背景设置为透明
xWalkView.setBackgroundColor(Color.WHITE)
xWalkView.setZorderTop(true);
surfaceview.getHolder().setFormat(SurfaceView.TRANSPARENT);
至此问题已解决,上最终效果图
如果对你有用,请点赞评论转发哦!