如何实现类似三星Air Command的Android全局背景模糊效果?
我完全懂你想要的效果——点击自己做的外部悬浮按钮后,整个手机屏幕的背景都能像三星Air Command那样一键模糊,而不是只能模糊自己应用内部的界面对吧?这个需求的核心是要实现系统级的全局模糊遮罩,毕竟普通的应用内模糊只能作用于自身界面,要覆盖其他应用甚至系统UI,得靠系统悬浮窗权限和全局覆盖层来实现。下面给你一步步拆解可行的解决方案:
解决方案步骤
1. 先搞定系统悬浮窗权限
要在其他应用和系统UI之上添加模糊层,必须先获取SYSTEM_ALERT_WINDOW权限,这是实现全局效果的前提。
检查权限状态
private fun hasOverlayPermission(): Boolean { return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { Settings.canDrawOverlays(this) } else { true // Android 6.0以下默认允许该权限 } }
引导用户授权
如果没有权限,就跳转到系统设置让用户开启:
private const val OVERLAY_PERMISSION_REQUEST_CODE = 1001 private fun requestOverlayPermission() { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { val intent = Intent( Settings.ACTION_MANAGE_OVERLAY_PERMISSION, Uri.parse("package:$packageName") ) startActivityForResult(intent, OVERLAY_PERMISSION_REQUEST_CODE) } }
2. 创建全局模糊遮罩层
我们需要通过WindowManager添加一个全屏的透明View,这个View就是用来显示模糊效果的,初始状态隐藏,点击悬浮按钮时切换显示/隐藏。
配置遮罩层的Window参数
要确保这个遮罩层能覆盖整个屏幕,并且层级足够高,不会被其他应用遮挡:
private fun createBlurOverlayParams(): WindowManager.LayoutParams { val windowType = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { // Android 8.0及以上用这个类型 WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY } else { // 低版本用系统弹窗类型 WindowManager.LayoutParams.TYPE_SYSTEM_ALERT } return WindowManager.LayoutParams( WindowManager.LayoutParams.MATCH_PARENT, WindowManager.LayoutParams.MATCH_PARENT, windowType, // 不拦截触摸事件,让用户可以点击模糊层下方的内容(如果需要的话) WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE or WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN, PixelFormat.TRANSLUCENT ).apply { gravity = Gravity.TOP or Gravity.START } }
实现模糊效果(分版本处理)
这里分两种方案,优先推荐Android 12+的原生模糊,低版本需要用截图模糊的方式:
方案A:Android 12+ 原生模糊(简单高效)
Android 12引入了原生的背景模糊API,不用截图就能实现全局模糊,性能更好:
private fun createBlurView(): View { val blurView = View(this) if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) { blurView.setBackgroundBlurRadius(20f) // 模糊半径,数值越大模糊效果越强 // 加一层半透明黑色,让模糊效果更明显 blurView.setBackgroundColor(Color.parseColor("#80000000")) } return blurView }
方案B:兼容Android 11及以下(截图模糊)
如果要支持低版本,需要先截取当前屏幕的截图,再对截图进行模糊处理,最后把模糊后的图片作为遮罩层的背景:
private fun createBlurViewForOldVersions(): View { val blurView = ImageView(this) // 先截取屏幕截图 val screenBitmap = takeScreenScreenshot() // 对截图进行模糊处理 val blurredBitmap = blurBitmap(screenBitmap, this) blurView.setImageBitmap(blurredBitmap) return blurView } // 截取当前屏幕截图(Android 10+需要MEDIA_PROJECTION权限) private fun takeScreenScreenshot(): Bitmap { val windowManager = getSystemService(WINDOW_SERVICE) as WindowManager val display = windowManager.defaultDisplay val screenSize = Point() display.getSize(screenSize) val bitmap = Bitmap.createBitmap(screenSize.x, screenSize.y, Bitmap.Config.ARGB_8888) val canvas = Canvas(bitmap) // 注意:Android 10及以上截取屏幕需要MEDIA_PROJECTION权限,需要额外引导用户授权 canvas.drawBitmap(bitmap, 0f, 0f, null) return bitmap } // 用RenderScript实现Bitmap模糊 private fun blurBitmap(sourceBitmap: Bitmap, context: Context): Bitmap { val rs = RenderScript.create(context) val inputAllocation = Allocation.createFromBitmap(rs, sourceBitmap) val outputAllocation = Allocation.createTyped(rs, inputAllocation.type) val blurScript = ScriptIntrinsicBlur.create(rs, Element.U8_4(rs)) blurScript.setRadius(20f) blurScript.setInput(inputAllocation) blurScript.forEach(outputAllocation) outputAllocation.copyTo(sourceBitmap) rs.destroy() return sourceBitmap }
注意:Android 10及以上截取屏幕需要
MEDIA_PROJECTION权限,这一步需要单独引导用户授权,流程会比原生模糊复杂一些,所以如果你的目标版本是Android 12+,优先用方案A。
3. 绑定悬浮按钮的点击逻辑
最后在你的悬浮按钮点击事件里,控制模糊遮罩层的显示和隐藏:
private lateinit var windowManager: WindowManager private lateinit var blurOverlay: View private var isBlurVisible = false override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) windowManager = getSystemService(WINDOW_SERVICE) as WindowManager // 根据系统版本创建对应的模糊View blurOverlay = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) { createBlurView() } else { createBlurViewForOldVersions() } // 假设你已经有了悬浮按钮的创建代码,这里绑定点击事件 val floatButton = createYourFloatButton() floatButton.setOnClickListener { if (isBlurVisible) { // 隐藏模糊层 windowManager.removeView(blurOverlay) } else { // 显示模糊层 windowManager.addView(blurOverlay, createBlurOverlayParams()) } isBlurVisible = !isBlurVisible } } // 这里替换成你自己的悬浮按钮创建逻辑 private fun createYourFloatButton(): View { val button = Button(this) button.text = "模糊屏幕" // 配置悬浮按钮的Window参数... return button }
一些关键注意事项
- 权限兼容性:部分国产厂商(比如小米、华为)会对悬浮窗权限做额外限制,可能需要引导用户去系统设置的“应用权限”里手动开启。
- 性能优化:截图模糊会消耗较多系统资源,尽量降低模糊半径,或者对截图进行缩小处理后再模糊。
- 交互逻辑:如果需要用户点击模糊层就关闭模糊效果,可以给blurOverlay添加点击监听,在点击时移除遮罩层。
内容的提问来源于stack exchange,提问作者shahid
相关产品推荐
相关产品推荐

