You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何用Java实现Android中Overlay下方内容的模糊效果?

实现Android悬浮窗下方内容模糊(SDK29兼容方案)

核心思路

由于Android 10无法使用官方Window Blurs功能,且第三方库仅能处理View自身或已有Bitmap内容,无法直接模糊悬浮窗外部的其他应用内容,因此采用屏幕截图+局部模糊+悬浮窗层叠的方案:

  • 先获取屏幕截图权限,捕获当前屏幕内容
  • 裁剪出需要模糊的目标区域并执行模糊处理
  • 将模糊后的Bitmap作为悬浮窗底层背景,再叠加自定义UI

步骤1:申请屏幕截图权限

Android 10需通过MediaProjectionManager获取截图权限,在启动悬浮窗前完成权限请求:

// 在启动悬浮窗的Activity中添加以下代码
private static final int REQUEST_MEDIA_PROJECTION = 1001;
private MediaProjectionManager mProjectionManager;

private void requestScreenCapture() {
    mProjectionManager = (MediaProjectionManager) getSystemService(Context.MEDIA_PROJECTION_SERVICE);
    startActivityForResult(mProjectionManager.createScreenCaptureIntent(), REQUEST_MEDIA_PROJECTION);
}

@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
    super.onActivityResult(requestCode, resultCode, data);
    if (requestCode == REQUEST_MEDIA_PROJECTION && resultCode == RESULT_OK) {
        // 拿到权限后初始化悬浮窗
        OverlayManager2 overlayManager = new OverlayManager2(this, data);
        overlayManager.open();
    }
}

步骤2:修改OverlayManager2实现截图与模糊逻辑

更新你的悬浮窗管理类,加入截图、模糊处理及背景设置代码:

public class OverlayManager2 {
    private final Context context;
    private final View mView;
    private final WindowManager.LayoutParams mParams;
    private final WindowManager mWindowManager;
    private MediaProjection mMediaProjection;
    private ImageReader mImageReader;
    private DisplayMetrics mMetrics;

    @RequiresApi(api = Build.VERSION_CODES.O)
    @SuppressLint("InflateParams")
    public OverlayManager2(Context context, Intent projectionData) {
        this.context = context;
        // 初始化MediaProjection
        MediaProjectionManager projectionManager = (MediaProjectionManager) context.getSystemService(Context.MEDIA_PROJECTION_SERVICE);
        mMediaProjection = projectionManager.getMediaProjection(Activity.RESULT_OK, projectionData);

        mParams = new WindowManager.LayoutParams(
                WindowManager.LayoutParams.MATCH_PARENT,
                WindowManager.LayoutParams.MATCH_PARENT,
                WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY,
                WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN | WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL,
                PixelFormat.TRANSLUCENT
        );

        LayoutInflater layoutInflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
        // 布局需包含用于显示模糊背景的ImageView(id为blur_bg)
        mView = layoutInflater.inflate(R.layout.pin_activity, null);

        mParams.gravity = Gravity.CENTER;
        mWindowManager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
        mMetrics = context.getResources().getDisplayMetrics();
        // 生成模糊背景
        generateBlurBackground();
    }

    private void generateBlurBackground() {
        // 初始化ImageReader用于截图
        mImageReader = ImageReader.newInstance(mMetrics.widthPixels, mMetrics.heightPixels, PixelFormat.RGBA_8888, 1);
        mMediaProjection.createVirtualDisplay("ScreenCapture",
                mMetrics.widthPixels, mMetrics.heightPixels, mMetrics.densityDpi,
                DisplayManager.VIRTUAL_DISPLAY_FLAG_AUTO_MIRROR,
                mImageReader.getSurface(), null, null);

        // 监听截图获取事件
        mImageReader.setOnImageAvailableListener(reader -> {
            try (Image image = reader.acquireLatestImage()) {
                if (image != null) {
                    Image.Plane[] planes = image.getPlanes();
                    ByteBuffer buffer = planes[0].getBuffer();
                    int pixelStride = planes[0].getPixelStride();
                    int rowStride = planes[0].getRowStride();
                    int rowPadding = rowStride - pixelStride * mMetrics.widthPixels;

                    // 将Image转为Bitmap
                    Bitmap bitmap = Bitmap.createBitmap(mMetrics.widthPixels + rowPadding / pixelStride, mMetrics.heightPixels, Bitmap.Config.ARGB_8888);
                    bitmap.copyPixelsFromBuffer(buffer);
                    // 裁剪需要模糊的区域(替换为你实际需要的坐标范围)
                    Rect blurRect = new Rect(
                            mMetrics.widthPixels / 4,
                            mMetrics.heightPixels / 4,
                            mMetrics.widthPixels * 3 / 4,
                            mMetrics.heightPixels * 3 / 4
                    );
                    Bitmap targetBitmap = Bitmap.createBitmap(bitmap, blurRect.left, blurRect.top, blurRect.width(), blurRect.height());
                    // 执行模糊处理
                    Bitmap blurredBitmap = blurBitmap(context, targetBitmap, 25);

                    // 设置模糊背景到布局
                    ImageView blurBg = mView.findViewById(R.id.blur_bg);
                    blurBg.setImageBitmap(blurredBitmap);
                    // 释放资源
                    bitmap.recycle();
                    targetBitmap.recycle();
                    mImageReader.close();
                    mMediaProjection.stop();
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
        }, null);
    }

    // 使用RenderScript实现高效模糊
    @SuppressLint("NewApi")
    private Bitmap blurBitmap(Context context, Bitmap bitmap, int radius) {
        RenderScript rs = RenderScript.create(context);
        Allocation input = Allocation.createFromBitmap(rs, bitmap);
        Allocation output = Allocation.createTyped(rs, input.getType());
        ScriptIntrinsicBlur script = ScriptIntrinsicBlur.create(rs, Element.U8_4(rs));
        script.setRadius(radius);
        script.setInput(input);
        script.forEach(output);
        output.copyTo(bitmap);
        rs.destroy();
        return bitmap;
    }

    public void open() {
        try {
            if (mView.getWindowToken() == null && mView.getParent() == null) {
                mWindowManager.addView(mView, mParams);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    // 销毁悬浮窗时释放资源
    public void close() {
        if (mView.getParent() != null) {
            mWindowManager.removeView(mView);
        }
        if (mMediaProjection != null) {
            mMediaProjection.stop();
        }
    }
}

步骤3:调整pin_activity布局结构

将模糊背景放在最底层,叠加自定义UI:

<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <!-- 模糊背景层 -->
    <ImageView
        android:id="@+id/blur_bg"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:scaleType="centerCrop"/>

    <!-- 你的自定义UI内容 -->
    <View
        android:layout_width="200dp"
        android:layout_height="200dp"
        android:layout_gravity="center"
        android:background="@color/white"
        android:alpha="0.8"/>

</FrameLayout>

注意事项

  • 权限必须:必须完成MEDIA_PROJECTION权限请求,否则无法捕获屏幕内容
  • 坐标适配:裁剪区域的坐标需根据实际屏幕尺寸和悬浮窗位置调整,避免模糊区域错位
  • 性能优化:截图和模糊操作存在性能开销,若需实时更新模糊效果,建议降低模糊半径或减少截图频率
  • 资源回收:悬浮窗关闭时需停止MediaProjection、回收Bitmap,避免内存泄漏

内容的提问来源于stack exchange,提问作者dasphalone

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.14 22:59:58