Android Fragment 嵌套XWalkView闪黑屏处理方案

先看一下问题


如图所示,在点击课件和商城两个按钮时,本来应该是加载两个不同的网页,但实际效果却不是这样,先添加的课件fragment会遮挡后添加的商城fragment,下面贴上fragment的切换代码
Android Fragment 嵌套XWalkView闪黑屏处理方案_第1张图片
初步判断是在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方式,
这样由系统控制其创建和删除,本以为这样就大功告成了,结果又高兴早了
Android Fragment 嵌套XWalkView闪黑屏处理方案_第2张图片
遮挡问题是解决了,但这么大一块黑屏,不能忍,接着上网搜资料,找到黑屏出现的原因
原因:
SurfaceView因为不同于一般的view,它有自己良好的缓冲以及数据存取机制,系统对他有特殊处理。当surfaceview第一次在当前activity上添加的时候,系统会给WindowManager重新排布局,relayout,这样就会黑一下,这个只会出现在第一次,以后再添加surfaceview时就不会黑屏了。而我们采用的添加删除fragment方式刚好引发这个问题
解决:首先给xWalkView设置背景颜色,然后将surfaceview背景设置为透明

xWalkView.setBackgroundColor(Color.WHITE)
xWalkView.setZorderTop(true);
surfaceview.getHolder().setFormat(SurfaceView.TRANSPARENT);

至此问题已解决,上最终效果图
Android Fragment 嵌套XWalkView闪黑屏处理方案_第3张图片
如果对你有用,请点赞评论转发哦!

有关Xwalkview技术分享,欢迎点击评论

你可能感兴趣的:(Android)