如何在Delphi安卓程序中通过代码实现全屏截图?
Delphi安卓应用全屏截图实现方案
我来帮你解决Delphi安卓应用全屏截图的问题——你当前的代码依赖PaintTo方法,确实只能捕获单个TControl控件的内容。要实现全屏截图(不管是自己App的整个界面,还是包含其他应用的系统级屏幕),我们得调用安卓原生API,因为FMX框架本身没有直接提供跨平台的全屏截图能力。下面给你两种实用方案:
一、方案1:截取当前应用的全屏(仅自身界面)
这种方式不需要系统级权限,只需要获取当前App的根View来生成截图,适合只需要捕获自己App界面的场景。
实现代码
uses FMX.Helpers.Android, Androidapi.JNI.GraphicsContentViewText, Androidapi.JNI.JavaTypes, Androidapi.JNI.Media, Androidapi.JNIBridge, FMX.Surfaces, FMX.Graphics; function CaptureAppScreen: TBitmap; var LActivity: JActivity; LRootView: JView; LBitmap: JBitmap; LSurface: TBitmapSurface; FScreenScale: Single; begin Result := nil; // 获取当前应用的Activity实例 LActivity := MainActivity; if not Assigned(LActivity) then Exit; // 获取App界面的根View LRootView := LActivity.getWindow.getDecorView.getRootView; LRootView.setDrawingCacheEnabled(True); try // 获取View的绘制缓存 LBitmap := LRootView.getDrawingCache; if Assigned(LBitmap) then begin // 获取屏幕缩放比例,适配不同分辨率 FScreenScale := Screen.Scale; // 将安卓原生Bitmap转换为FMX的TBitmap LSurface := TBitmapSurface.Create; try LSurface.Assign(JBitmapToBitmap(LBitmap)); Result := TBitmap.Create; Result.Assign(LSurface); // 调整截图尺寸到FMX的逻辑像素 Result.SetSize(Round(Result.Width / FScreenScale), Round(Result.Height / FScreenScale)); finally LSurface.Free; end; end; finally // 清理绘制缓存 LRootView.setDrawingCacheEnabled(False); LRootView.destroyDrawingCache; end; end;
使用说明
调用这个函数就能得到当前App整个界面的TBitmap对象,你可以直接显示或者保存到本地(保存前记得申请存储权限)。
二、方案2:系统级全屏截图(捕获整个屏幕,包括其他应用/状态栏)
如果需要截取整个安卓系统的屏幕(比如包含其他App、状态栏、导航栏等内容),就得使用安卓的MediaProjection API,这需要用户授权才能使用,是安卓5.0及以上版本的标准方案。
步骤1:请求截图权限
首先要向用户申请屏幕捕获权限:
uses Androidapi.JNI.App, Androidapi.JNI.Content, Androidapi.JNI.Os, FMX.Helpers.Android; const REQUEST_MEDIA_PROJECTION = 100; // 自定义请求码 procedure RequestScreenCapturePermission; var LMediaProjectionManager: JMediaProjectionManager; LIntent: JIntent; begin // 获取MediaProjection管理器 LMediaProjectionManager := TJMediaProjectionManager.Wrap( TAndroidHelper.Context.getSystemService(TJContext.JavaClass.MEDIA_PROJECTION_SERVICE)); // 创建权限请求Intent LIntent := LMediaProjectionManager.createScreenCaptureIntent; // 启动权限请求界面 MainActivity.startActivityForResult(LIntent, REQUEST_MEDIA_PROJECTION); end;
步骤2:处理权限回调
在主Form中注册回调,处理用户的权限选择结果:
uses FMX.Platform.Android, Androidapi.JNI.App; var FMediaProjection: JMediaProjection; // 全局变量保存MediaProjection实例 procedure TForm1.FormCreate(Sender: TObject); begin // 注册Activity结果回调 TAndroidHelper.Activity.setActivityResultCallback( TActivityResultCallback.Create(HandleActivityResult)); end; procedure TForm1.HandleActivityResult(RequestCode, ResultCode: Integer; Data: JIntent); var LMediaProjectionManager: JMediaProjectionManager; begin if RequestCode = REQUEST_MEDIA_PROJECTION then begin if ResultCode = TJActivity.JavaClass.RESULT_OK then begin // 用户同意授权,获取MediaProjection实例 LMediaProjectionManager := TJMediaProjectionManager.Wrap( TAndroidHelper.Context.getSystemService(TJContext.JavaClass.MEDIA_PROJECTION_SERVICE)); FMediaProjection := LMediaProjectionManager.getMediaProjection(ResultCode, Data); ShowMessage('权限申请成功,可以进行全屏截图了'); end else begin ShowMessage('用户拒绝了截图权限'); end; end; end;
步骤3:实现系统级截图
有了授权的MediaProjection实例后,就可以进行系统级截图了:
uses Androidapi.JNI.GraphicsContentViewText, Androidapi.JNI.Media, Androidapi.JNI.Os, FMX.Surfaces, FMX.Graphics; function CaptureSystemScreen: TBitmap; var LDisplayMetrics: JDisplayMetrics; LImageReader: JImageReader; LVirtualDisplay: JVirtualDisplay; LImage: JImage; LBitmap: JBitmap; LSurface: TBitmapSurface; FScreenScale: Single; begin Result := nil; // 检查是否已获取权限 if not Assigned(FMediaProjection) then begin ShowMessage('请先申请截图权限'); Exit; end; // 获取屏幕的真实尺寸和密度 LDisplayMetrics := TJDisplayMetrics.Create; MainActivity.getWindowManager.getDefaultDisplay.getRealMetrics(LDisplayMetrics); FScreenScale := Screen.Scale; // 创建用于接收截图的ImageReader LImageReader := TJImageReader.JavaClass.newInstance( LDisplayMetrics.widthPixels, LDisplayMetrics.heightPixels, TJImageFormat.JavaClass.RGBA_8888, 1); // 创建虚拟显示器,将屏幕内容投射到ImageReader的Surface LVirtualDisplay := FMediaProjection.createVirtualDisplay('ScreenCapture', LDisplayMetrics.widthPixels, LDisplayMetrics.heightPixels, LDisplayMetrics.densityDpi, TJDisplayManager.JavaClass.VIRTUAL_DISPLAY_FLAG_AUTO_MIRROR, LImageReader.getSurface, nil, nil); try // 获取最新的截图图像 LImage := LImageReader.acquireLatestImage; if Assigned(LImage) then try // 将Image转换为Bitmap LBitmap := TJBitmap.JavaClass.createBitmap(LImage.getWidth, LImage.getHeight, TJBitmap.Config.JavaClass.ARGB_8888); try LBitmap.copyPixelsFromBuffer(LImage.getPlanes[0].getBuffer); // 转换为FMX的TBitmap LSurface := TBitmapSurface.Create; try LSurface.Assign(JBitmapToBitmap(LBitmap)); Result := TBitmap.Create; Result.Assign(LSurface); // 调整到FMX逻辑像素尺寸 Result.SetSize(Round(Result.Width / FScreenScale), Round(Result.Height / FScreenScale)); finally LSurface.Free; end; finally LBitmap.recycle; // 回收安卓Bitmap资源 end; finally LImage.close; // 关闭Image资源 end; finally LVirtualDisplay.release; // 释放虚拟显示器 end; end;
三、重要注意事项
- 权限问题:两种方案如果需要保存截图到本地,都需要申请
READ_EXTERNAL_STORAGE和WRITE_EXTERNAL_STORAGE权限(安卓13+可以简化为READ_MEDIA_IMAGES/WRITE_MEDIA_IMAGES),记得在代码中动态申请权限。 - 资源清理:安卓原生的Bitmap、Image、VirtualDisplay等资源要及时释放,避免内存泄漏。
- 场景选择:如果只需要自己App的界面截图,优先用方案1,更简单不需要用户授权;如果需要系统级截图,再用方案2。
内容的提问来源于stack exchange,提问作者user13239154
相关产品推荐
相关产品推荐

