Delphi 表单何时真正对 Android 上的用户可见?

When is a Delphi form actually visible to the user on Android?

我正在开发的游戏在首次加载时会进行一些预处理,我想向用户显示 "loading %" 以表明游戏未冻结。

我正在尝试通过等待主窗体向用户显示然后在执行预处理时显示更新进度条来完成此操作。

为了预处理工作,我需要两条信息:
1. 表单处于最终大小(在整个显示中最大化)。
2. 表单对用户可见。

这是一个日志,显示了在我的游戏加载时触发的表单事件的顺序。请记住,我实际上并没有在任何这些事件中使用代码更改表单的大小:

FormCreate
FormResize, clientSize : 0x0
FormResize, clientSize : 0x0
FormResize, clientSize : 0x0
FormResize, clientSize : 0x0
FormShow
FormActivate
FormResize, clientSize : 360x640

目前,我尝试在最后一次调整大小时显示加载屏幕(表示窗体的实际上是显示的大小),但此时,主窗体仍然不可见(即使在 FormShow 之后和 FormActivate),我最终得到了 Android 的默认 "gray gradient" 屏幕,直到我的预处理代码完成后才显示,从不显示进度条。

我尝试在更新进度条后调用 "application.processmessages",但没有任何区别...

如何检测 Android 用户何时可以看到我的主表单?

[更新]
我创建了一个小应用程序来演示这个问题:
https://github.com/bLightZP/Test_Splash

此示例说明了如何使用 OnIdle,以及如何应该 实施它。

unit Unit1;

interface

uses
  System.SysUtils, System.Types, System.UITypes, System.Classes, System.Variants,
  FMX.Types, FMX.Controls, FMX.Forms, FMX.Graphics, FMX.Dialogs, FMX.Controls.Presentation, FMX.StdCtrls;

type
  TForm1 = class(TForm)
    ProgressBar1: TProgressBar;
  private
    FShown: Boolean;
    procedure ApplicationOnIdleHandler(Sender: TObject; var Done: Boolean);
    procedure ExecutePreProcessing;
    procedure ExecutePreProcessingSynchronized;
  public
    constructor Create(AOwner: TComponent); override;
  end;

var
  Form1: TForm1;

implementation

{$R *.fmx}

constructor TForm1.Create(AOwner: TComponent);
begin
  inherited;
  // Method 1: Use OnIdle to execute code when the main form is visible:
  Application.OnIdle := ApplicationOnIdleHandler;
  // Method 2: Use a thread to execute the preprocessing. Comment out the line above if using this method, and uncomment the line below
  // TThread.CreateAnonymousThread(ExecutePreProcessingSynchronized).Start;
end;

procedure TForm1.ApplicationOnIdleHandler(Sender: TObject; var Done: Boolean);
begin
  if not FShown then
  begin
    FShown := True;
    ExecutePreProcessing;
  end;
end;

procedure TForm1.ExecutePreProcessing;
var
  I: Integer;
begin
  for I := 1 to 100 do
  begin
    Sleep(50);
    ProgressBar1.Value := I;
    Application.ProcessMessages; // <---- Not the best idea
  end;
end;

procedure TForm1.ExecutePreProcessingSynchronized;
var
  I: Integer;
begin
  for I := 1 to 100 do
  begin
    Sleep(50);
    // Ensure UI updates happen in the main thread
    TThread.Synchronize(nil,
      procedure
      begin
        ProgressBar1.Value := I;
      end
    );
  end;
end;

end.

BecameActive事件(ApplicationEvent)怎么样? 使用这篇文章,用 Google 翻译 http://delphifmandroid.blogspot.com/2016/09/delphi-android.html

启动应用程序:

09-20 20:13:43.151: I/info(15893): FMX: Project1: FormCreate
09-20 20:13:43.151: I/info(15893): FMX: Project1: FormResize
09-20 20:13:43.151: I/info(15893): FMX: Project1: FormResize
09-20 20:13:43.161: I/info(15893): FMX: Project1: FormResize
09-20 20:13:43.161: I/info(15893): FMX: Project1: FormShow
09-20 20:13:43.161: I/info(15893): FMX: Project1: FormActivate
09-20 20:13:43.161: I/info(15893): FMX: Project1: Finished Launching
09-20 20:13:43.221: I/info(15893): FMX: Project1: FormPaint
09-20 20:13:43.231: I/info(15893): FMX: Project1: Became Active
09-20 20:13:43.261: I/info(15893): FMX: Project1: FormPaint
09-20 20:13:43.281: I/info(15893): FMX: Project1: FormPaint
09-20 20:13:43.311: I/info(15893): FMX: Project1: FormPaint