Android开发:如何实现带视图的Overlay并模糊可隐藏的后方区域
实现带可切换模糊背景的Android Overlay视图
嗨,刚接触Android开发的话,这个需求完全可以实现!我来一步步给你拆解怎么做:
一、先搞定基础Overlay的创建
Overlay本质是通过WindowManager添加的悬浮视图,首先得搞定权限问题:
- Android 6.0(API 23)及以上,要动态申请
SYSTEM_ALERT_WINDOW权限,用户需要在系统设置里给你的App开启「悬浮窗」权限 - 低于API 23的版本,直接在Manifest里声明权限就行
基础创建代码示例:
// 获取WindowManager实例 val windowManager = getSystemService(WINDOW_SERVICE) as WindowManager // 设置Overlay的布局参数 val params = WindowManager.LayoutParams( WindowManager.LayoutParams.WRAP_CONTENT, WindowManager.LayoutParams.WRAP_CONTENT, // 适配不同版本的窗口类型 if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY } else { WindowManager.LayoutParams.TYPE_PHONE }, // 窗口flags:不抢占焦点、允许覆盖全屏幕 WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE or WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN, PixelFormat.TRANSLUCENT ) // 创建Overlay容器,这里用LinearLayout示例,你可以换成其他布局 val overlayContainer = LinearLayout(this).apply { orientation = LinearLayout.VERTICAL // 往Overlay里加你需要的子视图,比如文本、按钮 addView(TextView(this@MainActivity).apply { text = "我是Overlay里的内容" setTextColor(Color.WHITE) setPadding(16.dpToPx(),16.dpToPx(),16.dpToPx(),16.dpToPx()) }) } // 把Overlay添加到窗口 windowManager.addView(overlayContainer, params)
注:上面的dpToPx()是个自定义扩展函数,用来处理单位转换,你可以自己实现一个简单的版本~
二、给Overlay后方添加模糊效果
这里有两种主流实现方式,选适合你的场景:
方式1:Android 12+ 原生BlurEffect(推荐)
API 31及以上系统提供了原生模糊API,性能更稳定、效果更统一:
// 创建模糊效果实例,radius值越大越模糊 val blurEffect = BlurEffect.Builder() .setBlurRadius(10f) .build() // 创建专门承载模糊效果的背景View,放到Overlay容器最底层 val blurBackgroundView = View(this).apply { layoutParams = ViewGroup.LayoutParams( ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT ) if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) { background = EffectDrawable(blurEffect) } } // 把模糊背景插入到Overlay容器的第一个位置(最底层) overlayContainer.addView(blurBackgroundView, 0)
方式2:兼容低版本的RenderScript模糊
如果要支持Android 12以下的版本,可以用RenderScript实现屏幕截图模糊:
// 对传入的Bitmap进行模糊处理 fun createBlurBitmap(bitmap: Bitmap, radius: Int): Bitmap { val renderScript = RenderScript.create(this) val input = Allocation.createFromBitmap(renderScript, bitmap) val output = Allocation.createTyped(renderScript, input.type) val blurScript = ScriptIntrinsicBlur.create(renderScript, Element.U8_4(renderScript)) blurScript.setRadius(radius.toFloat()) blurScript.setInput(input) blurScript.forEach(output) output.copyTo(bitmap) renderScript.destroy() return bitmap } // 先获取屏幕截图,再设置成模糊背景 val screenBitmap = getScreenBitmap() // 自己实现屏幕截图逻辑 val blurBitmap = createBlurBitmap(screenBitmap, 10) blurBackgroundView.background = BitmapDrawable(resources, blurBitmap)
三、控制模糊效果的显示/隐藏
这个很简单,两种思路任你选:
- 直接隐藏/显示模糊背景View:
// 隐藏模糊效果 blurBackgroundView.visibility = View.GONE // 重新显示模糊效果 blurBackgroundView.visibility = View.VISIBLE
- 动态修改模糊半径(过渡更平滑):
如果用的是原生BlurEffect,可以动态调整半径,设为0就相当于无模糊:
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) { val effectDrawable = blurBackgroundView.background as EffectDrawable val newBlurEffect = BlurEffect.Builder() .setBlurRadius(if (isBlurVisible) 10f else 0f) .build() effectDrawable.setEffect(newBlurEffect) }
一些注意事项
- 模糊效果比较消耗性能,尽量不要给超大View设置,或者降低模糊半径
- 悬浮窗权限一定要处理好,否则Overlay会显示不出来
- 如果你的Overlay需要处理触摸事件,记得调整
WindowManager.LayoutParams的flags,去掉FLAG_NOT_FOCUSABLE
内容的提问来源于stack exchange,提问作者Lucadmin




