You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多
文档控制台
免费开始使用

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)

三、控制模糊效果的显示/隐藏

这个很简单,两种思路任你选:

  1. 直接隐藏/显示模糊背景View
// 隐藏模糊效果
blurBackgroundView.visibility = View.GONE
// 重新显示模糊效果
blurBackgroundView.visibility = View.VISIBLE
  1. 动态修改模糊半径(过渡更平滑)
    如果用的是原生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

火山引擎 最新活动