bLight
bLight

Reputation: 865

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

The game that I'm developing does some pre-processing when first loading and I want to show the user a "loading %" to make it clear that the game is not frozen.

I am trying to do this by waiting for the main form to show for the user and then displaying an updating progress bar while performing the pre-processing.

For the pre-processing to work, I need two pieces of information:
1. The form is in it's final size (maximized across the entire display).
2. The form is visible to the user.

Here is a log showing the order of the form events being triggered as my game loads. Keep in mind that I'm not actually changing the form's size using code in any of these events:

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

Currently, I try to show the loading screen on the last resize (the one indicating the form is actually the size of the display), but at this point, the main form is still not visible (even after the FormShow and FormActivate) and I end up with the Android's default "gray gradient" screen showing until after my pre-processing code already finished, never showing the progress bar.

I tried calling "application.processmessages" after updating the progress bar, but it doesn't make a difference...

How do I detect when my main form is actually visible to the Android user?

[Update]
I created a small application to demonstrate this issue:
https://github.com/bLightZP/Test_Splash

Upvotes: 0

Views: 976

Answers (2)

alitrun
alitrun

Reputation: 1217

How about BecameActive event (ApplicationEvent)? Use this article, translate it with Google Translate http://delphifmandroid.blogspot.com/2016/09/delphi-android.html

Starting app:

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

Upvotes: 1

Dave Nottage
Dave Nottage

Reputation: 3602

This example illustrates using OnIdle, and also how it should be implemented.

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.

Upvotes: 3

Related Questions