如何将Android半透明覆盖层后方内容转为灰度(保留前景彩色)
解决方案
Android 12+(API 31及以上)
用RenderEffect可以直接实现需求,它能给透过悬浮窗看到的底层内容添加灰度滤镜,同时不影响悬浮窗内的前景UI:
- 给悬浮窗根布局设置灰度渲染效果:
val layoutRoot = findViewById<LinearLayout>(R.id.layout_root) // 创建灰度渲染效果,仅作用于透过View的背景内容 val grayscaleEffect = RenderEffect.createGrayscaleEffect() layoutRoot.setRenderEffect(grayscaleEffect)
- 调整布局结构,根布局设为透明背景,叠加半透明遮罩和彩色前景CardView:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:id="@+id/layout_root" android:layout_width="match_parent" android:layout_height="match_parent" android:background="@android:color/transparent" android:orientation="vertical"> <!-- 半透明覆盖层 --> <View android:layout_width="match_parent" android:layout_height="match_parent" android:background="#80000000"/> <!-- 彩色前景CardView --> <androidx.cardview.widget.CardView android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="center" android:background="@color/white"> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="彩色前景文本" android:textColor="@color/red"/> </androidx.cardview.widget.CardView> </LinearLayout>
Android 12以下版本
低版本无RenderEffect支持,可通过SurfaceView捕获底层画面并灰度化渲染,再叠加彩色UI:
- 布局结构(用FrameLayer堆叠层级):
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android" android:id="@+id/root_frame" android:layout_width="match_parent" android:layout_height="match_parent"> <!-- 显示灰度化底层画面的SurfaceView --> <SurfaceView android:id="@+id/gray_surface" android:layout_width="match_parent" android:layout_height="match_parent"/> <!-- 半透明覆盖层 --> <View android:layout_width="match_parent" android:layout_height="match_parent" android:background="#80000000"/> <!-- 彩色前景CardView --> <androidx.cardview.widget.CardView android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="center" android:background="@color/white"> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="彩色前景文本" android:textColor="@color/blue"/> </androidx.cardview.widget.CardView> </FrameLayout>
- 代码实现灰度渲染逻辑:
SurfaceView graySurface = findViewById(R.id.gray_surface); graySurface.getHolder().addCallback(new SurfaceHolder.Callback() { @Override public void surfaceCreated(SurfaceHolder holder) { startGrayRendering(holder); } @Override public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {} @Override public void surfaceDestroyed(SurfaceHolder holder) {} }); private void startGrayRendering(SurfaceHolder holder) { new Thread(() -> { Canvas canvas; while (!Thread.interrupted()) { canvas = holder.lockCanvas(); if (canvas != null) { Bitmap screenBitmap = captureScreen(); if (screenBitmap != null) { // 创建灰度Paint Paint grayPaint = new Paint(); ColorMatrix colorMatrix = new ColorMatrix(); colorMatrix.setSaturation(0); grayPaint.setColorFilter(new ColorMatrixColorFilter(colorMatrix)); // 绘制灰度化截图 canvas.drawBitmap(screenBitmap, 0, 0, grayPaint); screenBitmap.recycle(); } holder.unlockCanvasAndPost(canvas); } try { Thread.sleep(16); } catch (InterruptedException e) { Thread.currentThread().interrupt(); } } }).start(); } // 用MediaProjection实现屏幕捕获(需动态申请权限,具体实现参考官方文档) private Bitmap captureScreen() { // 此处实现MediaProjection屏幕捕获逻辑 return null; }
关键注意事项
- 悬浮窗需申请
SYSTEM_ALERT_WINDOW权限(Android 6.0+需手动授权)。 - Android 12+的
RenderEffect方案性能最优,无需额外屏幕捕获逻辑。 - 低版本推荐用
MediaProjection捕获屏幕,READ_FRAME_BUFFER为系统级权限,普通APP无法获取。
内容的提问来源于stack exchange,提问作者Flyview
相关产品推荐
相关产品推荐

