简体   繁体   中英

What is the best way to create a lot of controls at runtime to minimize the load time in firemonkey?

I'm having problems of performance speed in an Android app in Delphi XE7 with Firemonkey using JSON XSuperObject and data retrieved from a web API.

My scenario is a system ranking table that creates 29 controls at runtime with each iteration, sometimes the number of players should be more than 90 so for each iteration (90 * 27) it create 2610~ controls at runtime. Tested on a Windows platform it took 3 secs., but in a mobile platform with Android, it took 17 secs. I'm aware that it would affect to the performance of the app, but I don't knew of the significant difference of load time of Windows and Android.

So is there any way to reduce the load time or optimize the code below for a best performance?, thanks.

Kind Regards.

{Controls to create at runtime;

1 TLayout as parent of the controls (1 control).

1 TLine at bottom of TLayout to separate layouts (+1 control)

1 TPanel with a TImage for the player avatar and a TLabel for the player name (+3 controls).

1 TPanel with a TButton to display details (+2 controls).

1 TPanel with a THorzScrollBox containing 10 TLabels inside of 10 TRectangle as parent  to scroll and display the points of the round (+22 controls).

Overall of 29 controls to create at runtime in each iteration.}

procedure TForm1.RankingClick(Sender: TObject);
var
        JsonObjHC: ISuperObject;
        JsonArrHC: ISuperArray;
        JsonStrHC, URL_IRkC, Pais: String;
        Retriever: TIdHTTP;
        x, z: integer;
        Before, After, Total:  TDateTime;
begin
        URL_IRkC := 'web api URL here ….';
        Retriever := TIdHTTP.Create(nil);
        try
             JsonStrHC := Retriever.Get(URL_IRkC);
        finally
             Retriever.DisposeOf;
        end;
        JsonArrHC := SA(JsonStrHC);
        Before := Time;  // to check seconds before loop starts
        LytMainRk.BeginUpdate;   //Main layout that contains all controls.
        for x := 0 to JsonArrHC.Length-1 do
        begin
              JsonObjHC := JsonArrHC.O[x];
              // proceddure that creates controls in each iteration   
              Ranking_Table (JsonObjHC.S['Image'], JsonObjHC.S['Name'], JsonObjHC.I['R1'], JsonObjHC.I['R2'], JsonObjHC.I['R3'], JsonObjHC.I['R4'], JsonObjHC.I['R5'], JsonObjHC.I['R6'], JsonObjHC.I['R7'], JsonObjHC.I['R8'], JsonObjHC.I['R9'], JsonObjHC.I['R10'], x, z);
              inc(z, 28);
        end;
        LytMainRk.EndUpdate;
        Total := Before - After;
        SecondsBetween(ShowMessage(DateToStr(Total)); // seconds after loop finishes, 3 secs. on Windows, 17 secs. on Android
end;


procedure TForm1.Ranking_Table(Avatar, NameP: String;  R1, R2, R3, R4, R5, R6, R7, R8, R9, R10, x, z: integer);
var
    RectHC_R1, RectHC_R2, RectHC_R3, RectHC_R4, RectHC_R5, RectHC_R6, RectHC_R7, RectHC_R8, RectHC_R9, RectHC_R10: TRectangle;
    PanelHC, PanelHC1, PanelHC2: TPanel;
    ImageAvatarHC: TImage;
    LineaHC: TLine;
    LNameHC, LabelHC_R1, LabelHC_R2, LabelHC_R3, LabelHC_R4, LabelHC_R5, LabelHC_R6, LabelHC_R7, LabelHC_R8, LabelHC_R9, LabelHC_R10: TLabel;
    LayoutHC: TLayout;
    BotonHC: TButton;
    HorzScrollBoxHC: THorzScrollBox;
begin
    LayoutHC := TLayout.Create(Self);
    LayoutHC.Name := 'LayoutHC' + x.ToString;
    LayoutHC.Align := TAlignLayout.Top;
    LayoutHC.Margins.Left := 6;
    LayoutHC.Margins.Right := 6;
    LayoutHC.Margins.Top := 0;
    LayoutHC.Position.Y := z;
    LayoutHC.Height := 28;
    LayoutHC.Parent := LyHistorialC;
    LineaHC := TLine.Create(Self);
    LineaHC.Name := 'LineaHC' + x.ToString;
    LineaHC.Align := TAlignLayout.MostBottom;
    LineaHC.LineType := TLineType.Top;
    LineaHC.Height := 1;
    LineaHC.Stroke.Color := $FF708090;
    LineaHC.Stroke.Thickness := 1;
    LineaHC.Parent := LayoutHC;
    PanelHC := TPanel.Create(Self);
    PanelHC.Name := 'PanelHC_' + x.ToString;
    PanelHC.Align := TAlignLayout.MostLeft;
    PanelHC.Width := 126;
    PanelHC.Parent := LayoutHC;
    ImageAvatarHC := TImage.Create(Self);
    ImageAvatarHC.Name := ' ImageAvatarHC ' + x.ToString;
    ImageAvatarHC.Align := TAlignLAyout.Left;
    ImageAvatarHC.Margins.Left := 4;
    ImageAvatarHC.Margins.Right := 2;
    ImageAvatarHC.Width := 28;
    load_image_from_resource(ImageAvatarHC, Avatar);
    ImageAvatarHC.Parent := PanelHC;
    LNameHC := TLabel.Create(Self);
    LNameHC.Name := ' LNameHC ' + x.ToString;
    LNameHC.Align := TAlignLayout.Client;
    LNameHC.Margins.Left := 2;
    LNameHC.AutoSize := True;
    LNameHC.TextSettings.FontColor := $FF000000;
    LNameHC.TextSettings.Font.Size := 10;
    LNameHC.StyledSettings := [];
    LNameHC.Text := NameP;
    LNameHC.Parent := PanelHC;
    PanelHC1 := TPanel.Create(Self);
    PanelHC1.Name := 'PanelHC1' + x.ToString;
    PanelHC1.Align := TAlignLayout.Left;
    PanelHC1.Width := 26;
    PanelHC1.Parent := LayoutHC;
    BotonHC := TButton.Create(Self);
    BotonHC.Name := 'BotonHC' + x.ToString;
    BotonHC.Align := TAlignLayout.Client;
    BotonHC.TextSettings.Font.Style := [TFontStyle.fsBold];
    BotonHC.Text := '>';
    BotonHC.OnClick := ClickHistorialC;
    BotonHC.Parent := PanelHC1;
    PanelHC2 := TPanel.Create(Self);
    PanelHC2.Name := 'PanelHC2' + x.ToString;
    PanelHC2.Align := TAlignLayout.Client;
    PanelHC2.Parent := LayoutHC;
    HorzScrollBoxHC := THorzScrollBox.Create(Self);
    HorzScrollBoxHC.Name := 'HorzScrollBoxHC' + x.ToString;
    HorzScrollBoxHC.Align := TAlignLayout.Client;
    HorzScrollBoxHC.ShowScrollBars := False;
    HorzScrollBoxHC.Parent := PanelHC2;

    RectHC_R1 := TRectangle.Create(Self);
    RectHC_R1.Name := 'RectHC_R1' + x.ToString;
    RectHC_R1.Align := TAlignLayout.Left;
    RectHC_R1.Fill.Color := $FF1E90FF;
    RectHC_R1.Margins.Bottom := 2;
    RectHC_R1.Margins.Left := 2;
    RectHC_R1.Margins.Top := 2;
    RectHC_R1.Sides := [];
    RectHC_R1.Stroke.Kind := TBrushKind.None;
    RectHC_R1.Width := 23;
    RectHC_R1.XRadius := 4;
    RectHC_R1.YRadius := 4;
    RectHC_R1.Parent := HorzScrollBoxHC;
    LabelHC_R1 := TLabel.Create(Self);
    LabelHC_R1.Name := ' LabelHC_R1' + x.ToString;
    LabelHC_R1.Align := TAlignLayout.Client;
    LabelHC_R1.AutoSize := True;
    LabelHC_R1.TextSettings.FontColor := $FFFFFFFF;
    LabelHC_R1.TextSettings.Font.Size := 11;
    LabelHC_R1.TextSettings.HorzAlign := TTextAlign.Center;
    LabelHC_R1.StyledSettings := [];
    LabelHC_R1.Text := R1.ToString;
    LabelHC_R1.Parent := RectHC_R1;
   {  ………………….
              Up to 8 TRectangles and TLabels to create here…
             …………………….  }
    RectHC_R10 := TRectangle.Create(Self);
    RectHC_R10.Name := 'RectHC_R10' + x.ToString;
    RectHC_R10.Align := TAlignLayout.Left;
    RectHC_R10.Fill.Color := $FF1E90FF;
    RectHC_R10.Margins.Bottom := 2;
    RectHC_R10.Margins.Left := 2;
    RectHC_R10.Margins.Top := 2;
    RectHC_R10.Sides := [];
    RectHC_R10.Stroke.Kind := TBrushKind.None;
    RectHC_R10.Width := 23;
    RectHC_R10.XRadius := 4;
    RectHC_R10.YRadius := 4;
    RectHC_R10.Parent := HorzScrollBoxHC;
    LabelHC_R10 := TLabel.Create(Self);
    LabelHC_R10.Name := ' LabelHC_R10' + x.ToString;
    LabelHC_R10.Align := TAlignLayout.Client;
    LabelHC_R10.AutoSize := True;
    LabelHC_R10.TextSettings.FontColor := $FFFFFFFF;
    LabelHC_R10.TextSettings.Font.Size := 11;
    LabelHC_R10.TextSettings.HorzAlign := TTextAlign.Center;
    LabelHC_R10.StyledSettings := [];
    LabelHC_R10.Text := R10.ToString;
    LabelHC_R10.Parent := RectHC_R1;
end;

You will never get acceptable speed with that many controls, especially on a mobile device.

For rectangles, text, lines, ... I'd simply use custom painting. You could use a PaintBox with an OnPaint event.

procedure TForm1.PaintBox1Paint(Sender: TObject; Canvas: TCanvas);
begin
  Canvas.Fill.Color := $FF1E90FF;
  Canvas.FillRect(RectF(10, 10, 200, 50), 4, 4, [], 1.0);
  Canvas.Fill.Color := $FFFFFFFF;
  Canvas.Font.Size := 13.0;
  Canvas.FillText(RectF(10, 10, 200, 50), 'My Text', True, 1.0, [], TTextAlign.Center, TTextAlign.Center);
end;

This event will be called whenever a screen update is needed. If your data has changed and you want to update the screen, call PaintBox1.InvalidateRect({...area that needs to be updated}); . This will trigger a screen update.

A button is a bit more tricky, you'd have to use the OnMouseDown and OnMouseUp events and manually evaluate the mouse clicks.

In the long run, it will probably make sense to have your own control that derives from TControl or TStyledControl that paints in an overridden Paint method.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM