SurfaceView透明且不覆盖Android原生View?

2022-03-14  本文已影响0人  youthyJ

引子

最近有项目开始使用SurfaceView, 这个View跟Android中其他的View有很大的不同, 所以在使用的时候遇到了一些问题, 首当其冲就是它的背景问题.

在我的项目中, 我希望实现如下的效果:

视图层级:

  1. 原生View
  2. SurfaceView
  3. Activity背景

因为希望透过SurfaceView我能看到Activity的背景, 所以我把SurfaceView的像素设置为半透明:

surfaceView.holder.setFormat(PixelFormat.TRANSPARENT)

同时为了不挡住原生View, 我把SurfaceView的层级调低:

surfaceView.setZOrderOnTop(false)

然后满心期待视图能够如我所愿的显示.

如果你有过类似的经历, 你一定会大失所望...
因为运行起来之后你会看到一片黑漆漆的背景...

这是为什么呢?
根据官方的说法, SurfaceView实际上会穿透Activity的背景, 就像在上面凿了个洞(the SurfaceView punches a hole in its window to allow its surface to be displayed. ), 然后因为Activity的Window不是透明的, 导致不能透过这洞看到后面的东西, 所以就显示成黑色的了.

这个问题一直困扰了我很久, 在网上搜索之后发现很多人建议使用TextureView来替代SurfaceView, 但是说实话, TextureView总感觉没有SurfaceView快...

看到这里相信你已经知道我可能有解决的方案了~

解决方案

既然我们知道了SurfaceView是在原来的Activity的Window上凿了个洞, 而透过这个洞后面看不到东西, 那我们在这个洞后面找一个弄一个SurfaceView补上不就行了~
以下就是新版的层级关系:

视图层级:

  1. 原生View
  2. 目标SurfaceView
  3. 绘制背景的SurfaceView

关于绘制背景的SurfaceView如何绘制不是本文的讨论重点, 大家可以参考以下代码:

svBackground.apply {
    setZOrderOnTop(false)
    setZOrderMediaOverlay(false)
    holder.setFormat(PixelFormat.OPAQUE)
    holder.addCallback(object : SurfaceHolder.Callback {
        val bgColor = 0x303030

        override fun surfaceCreated(holder: SurfaceHolder) {}

        override fun surfaceChanged(
            holder: SurfaceHolder,
            format: Int,
            width: Int,
            height: Int
        ) {
            val canvas = holder.lockCanvas()
            try {
                canvas?.drawARGB(bgColor.alpha, bgColor.red, bgColor.green, bgColor.blue)
            } finally {
                holder.unlockCanvasAndPost(canvas)
            }
        }

        override fun surfaceDestroyed(holder: SurfaceHolder) {}

    })
}

同时为了让我们的目标SurfaceView绘制背景的SurfaceView的层级高, 我们需要为它补充一个设置:

surfaceView.setZOrderMediaOverlay(true)

好了, 现在的话他就不是一个纯黑背景的界面了~

上一篇下一篇

猜你喜欢

热点阅读