如何用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
相关产品推荐
相关产品推荐

