SurfaceView透明且不覆盖Android原生View?
引子
最近有项目开始使用SurfaceView, 这个View跟Android中其他的View有很大的不同, 所以在使用的时候遇到了一些问题, 首当其冲就是它的背景问题.
在我的项目中, 我希望实现如下的效果:
视图层级:
- 原生View
- SurfaceView
- 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补上不就行了~
以下就是新版的层级关系:
视图层级:
- 原生View
- 目标SurfaceView
- 绘制背景的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)
好了, 现在的话他就不是一个纯黑背景的界面了~