为什么在 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;
我在下面的代码中在 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;