为什么在 Firemonkey 中,当运行时创建控件在完成迭代时显示?

Why in Firemonkey when create controls at runtime are display when finish iteration?

我在下面的代码中在 runtine 中创建了 100 多个矩形;

var
  RectT: TRectangle;
  MyThread: TThread;
  Layout1: TLayout;
begin
  MyThread := TThread.CreateAnonymousThread(procedure()
  begin
        TThread.Synchronize(nil, procedure()
        var
            z, i: integer;
        begin
            z := 0;
            for i := 0 to 99 do
            begin
                 RectT := TRectangle.Create(Self);
                 RectT.Name := 'Rectangle' + IntToStr(i);
                 RectT.Align := TAlignLayout.Top;
                 RectT.Margins.Top := 6;
                 RectT.Position.Y := z;
                 RectT.Height := 20;
                 RectT.Parent := Layout1;
                 if (i mod 10) = 0 then Layout1.UpdateEffects;
                 inc(z, 20);
            end;
        end);
  end);
  MyThread.FreeOnTerminate := True;
  MyThread.Start;

结束;

为什么矩形创建时不显示,所有矩形迭代完成后才显示?

如果这段代码是 运行 在主线程上(这似乎是因为你没有提到任何线程)那么 FMX 运行时第一次有机会直观地更新 UI 是当您的代码本身完成时 运行.

如果您希望 UI 更新以显示添加的矩形,那么您将需要重新编写此代码以使用允许 UI 有机会重新绘制的方法定期。

更新

您在问题中更新的代码现在涉及一个线程。但是,在您发布的代码中,您 Synchronize() all 该线程中的工作。 同步d 代码在主线程中运行,因此同步所有工作的结果是完全消除线程的任何好处。

然而你快到了。

对您发布的代码做一个小改动,以便在线程中添加布局子对象,仅定期同步布局对象本身的重绘,然后您将得到您想要的结果:

var
  MyThread: TThread;
begin
  MyThread := TThread.CreateAnonymousThread
  (
    procedure()
    var
      z, i: integer;
      RectT: TRectangle;
    begin
      z := 0;
      for i := 0 to 999 do
      begin
        RectT := TRectangle.Create(Self);
        RectT.Name := 'Rectangle' + IntToStr(i);
        RectT.Align := TAlignLayout.Top;
        RectT.Margins.Top := 6;
        RectT.Position.Y := z;
        RectT.Height := 20;
        RectT.Parent := Layout1;

        TThread.Synchronize(nil, procedure()
                                 begin
                                   Layout1.Repaint;
                                 end);

        inc(z, 20);
      end;
    end
  );

  MyThread.FreeOnTerminate := True;
  MyThread.Start;
end;

我已将此方法演示中的子对象数量增加到 999,因为 99 不足以看到任何明显的性能变化。

如所写,在添加 每个 矩形后,上面的代码也会重新绘制,但这可以通过类似于您发布的代码的方式轻松修改,以便重新绘制布局仅在添加 "batches" 个矩形之后:

if (i mod 10) = 0 then 
  TThread.Synchronize(nil, procedure()
                           begin
                             Layout1.Repaint;
                           end);

这是一种简单的方法,解决了更新 UI 的直接问题,以显示使用这个非常简单的测试用例对 UI 所做的一些背景更改的进度。对于您的具体情况,这是否真的是最合适的方法只有您才能真正说清楚。

首先,您需要在一个线程中移动 for 循环,并在 Synchronize 调用中创建矩形,就像 Deltics 所做的那样。不同的是不需要Repaint的调用,需要使用currentthread传递调用同步

试试这个(在 Button 的 OnClick 事件中):

procedure TForm4.Button1Click(Sender: TObject);
begin
  TThread.CreateAnonymousThread(procedure
  var
    I,z: Integer;
    Total: Integer;
  begin
    Total := 0;
    for I := 1 to 99 do
    begin
        TThread.Synchronize (TThread.CurrentThread,
          procedure
          var
            RectT: TRectangle;
          begin
            RectT := TRectangle.Create(Self);
            RectT.Name := 'Rectangle' + IntToStr(i);
            RectT.Align := TAlignLayout.Top;
            RectT.Margins.Top := 6;
            RectT.Position.Y := z;
            RectT.Height := 20;
            RectT.Parent := Layout1;
            Inc(z, 20);
          end);
    end;
  end).Start;
end;