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

Android编程:如何检测Overlay并捕获含其与Toast的全屏截图?

实现全屏截图(含Overlay/Toast)+ Overlay监听方案

一、全屏截图核心实现:MediaProjection API

你之前的代码只能捕获当前Activity视图,原因是window.decorView仅对应自身App界面。要捕获包含Overlay、Toast在内的整个屏幕,必须使用Android官方提供的MediaProjection API——无需root权限,能覆盖所有屏幕显示元素。

1. 申请MediaProjection权限

首先启动系统权限申请界面:

private val REQUEST_CODE_SCREEN_CAPTURE = 1001

fun requestScreenCapturePermission() {
    val mediaProjectionManager = getSystemService(MEDIA_PROJECTION_SERVICE) as MediaProjectionManager
    val intent = mediaProjectionManager.createScreenCaptureIntent()
    startActivityForResult(intent, REQUEST_CODE_SCREEN_CAPTURE)
}

// 处理权限申请结果
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
    super.onActivityResult(requestCode, resultCode, data)
    if (requestCode == REQUEST_CODE_SCREEN_CAPTURE && resultCode == RESULT_OK) {
        val mediaProjection = (getSystemService(MEDIA_PROJECTION_SERVICE) as MediaProjectionManager)
            .getMediaProjection(resultCode, data!!)
        startScreenCapture(mediaProjection)
    }
}

2. 执行全屏截图

通过ImageReader捕获屏幕帧并转换为Bitmap保存:

private fun startScreenCapture(mediaProjection: MediaProjection) {
    val displayMetrics = resources.displayMetrics
    val screenWidth = displayMetrics.widthPixels
    val screenHeight = displayMetrics.heightPixels

    // 创建ImageReader,指定屏幕分辨率和像素格式
    val imageReader = ImageReader.newInstance(screenWidth, screenHeight, PixelFormat.RGBA_8888, 1)

    // 创建VirtualDisplay,将屏幕内容投射到ImageReader的Surface
    val virtualDisplay = mediaProjection.createVirtualDisplay(
        "ScreenCapture",
        screenWidth, screenHeight, displayMetrics.densityDpi,
        DisplayManager.VIRTUAL_DISPLAY_FLAG_AUTO_MIRROR,
        imageReader.surface, null, null
    )

    // 监听新帧生成
    imageReader.setOnImageAvailableListener({ reader ->
        val image = reader.acquireLatestImage() ?: return@setOnImageAvailableListener
        val planes = image.planes
        val buffer = planes[0].buffer
        val pixelStride = planes[0].pixelStride
        val rowStride = planes[0].rowStride
        val rowPadding = rowStride - pixelStride * screenWidth

        // 转换为Bitmap并裁剪多余padding
        val bitmap = Bitmap.createBitmap(
            screenWidth + rowPadding / pixelStride,
            screenHeight, Bitmap.Config.ARGB_8888
        )
        bitmap.copyPixelsFromBuffer(buffer)
        val croppedBitmap = Bitmap.createBitmap(bitmap, 0, 0, screenWidth, screenHeight)
        bitmap.recycle()

        // 保存截图文件
        saveBitmapToFile(croppedBitmap)

        // 释放资源
        image.close()
        imageReader.close()
        virtualDisplay.release()
        mediaProjection.stop()
    }, Handler(Looper.getMainLooper()))
}

private fun saveBitmapToFile(bitmap: Bitmap) {
    val mPath = "${cacheDir.absolutePath}/temp_${System.currentTimeMillis()}.png"
    val imageFile = File(mPath)
    FileOutputStream(imageFile).use { outputStream ->
        bitmap.compress(Bitmap.CompressFormat.PNG, 100, outputStream)
    }
    bitmap.recycle()
}

二、监听Overlay窗口显示

要检测其他应用的Overlay窗口,需使用AccessibilityService,它能监听系统所有窗口的状态变化。

1. 配置AccessibilityService

在res/xml目录下创建accessibility_service_config.xml:

<?xml version="1.0" encoding="utf-8"?>
<accessibility-service xmlns:android="http://schemas.android.com/apk/res/android"
    android:accessibilityEventTypes="typeWindowStateChanged"
    android:accessibilityFeedbackType="feedbackGeneric"
    android:accessibilityFlags="flagDefault"
    android:canRetrieveWindowContent="true"
    android:description="@string/accessibility_service_desc"/>

在AndroidManifest.xml中注册服务:

<service
    android:name=".OverlayMonitorService"
    android:permission="android.permission.BIND_ACCESSIBILITY_SERVICE">
    <intent-filter>
        <action android:name="android.accessibilityservice.AccessibilityService"/>
    </intent-filter>
    <meta-data
        android:name="android.accessibilityservice"
        android:resource="@xml/accessibility_service_config"/>
</service>

2. 实现Overlay监听逻辑

创建OverlayMonitorService类:

class OverlayMonitorService : AccessibilityService() {
    override fun onAccessibilityEvent(event: AccessibilityEvent) {
        if (event.eventType == AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED) {
            val windowInfo = event.source?.windowInfo ?: return
            // 判断是否为Overlay类型窗口
            if (windowInfo.type == AccessibilityWindowInfo.TYPE_APPLICATION_OVERLAY ||
                windowInfo.type == AccessibilityWindowInfo.TYPE_SYSTEM_OVERLAY) {
                // 检测到Overlay显示,触发截图逻辑
                Handler(Looper.getMainLooper()).post {
                    // 调用之前的截图方法,若权限已过期需重新申请
                    // requestScreenCapturePermission()
                }
            }
        }
    }

    override fun onInterrupt() {}
}

关键注意事项

  • MediaProjection权限需用户手动授权,且设备重启后权限失效,需重新申请。
  • AccessibilityService需要用户在系统设置中手动开启(路径:设置→无障碍→你的应用服务)。
  • 建议添加截图防抖逻辑,避免因窗口频繁切换触发过多截图操作。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.12 01:15:35