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

如何在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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.11 07:28:07