Я создал более 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;

Конец;

Почему прямоугольник не отображался при его создании, а отображается только после завершения итерации всех прямоугольников?

0
Juande 20 Мар 2017 в 00:39

2 ответа

Лучший ответ

Во-первых, вам нужно переместить цикл for в одном потоке и создать прямоугольники в вызове Synchronize, как это сделал Deltics. Разница в том, что вам не нужен вызов Repaint, и вам нужно использовать текущий поток для передачи вызова для синхронизации.

Попробуйте это (в событии 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;
2
John Kouraklis 21 Мар 2017 в 22:11

Если этот код выполняется в главном потоке (что, по-видимому, имеет место, поскольку вы не упоминаете ни о каком потоке), то первая возможность, которую среда выполнения FMX имеет для визуального обновления пользовательского интерфейса, - это когда ваш код завершит свою работу.

Если вы хотите, чтобы пользовательский интерфейс обновлялся для отображения прямоугольников по мере их добавления, то вам нужно будет переписать это, чтобы использовать подход, позволяющий пользовательскому интерфейсу периодически перерисовываться.

ОБНОВИТЬ

Ваш обновленный код в вопросе теперь включает в себя поток. Однако в опубликованном вами коде вы Synchronize () все работаете в этой теме. Синхронизировать 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 было недостаточно, чтобы увидеть какие-либо заметные изменения в производительности.

Как уже было написано, вышеприведенный код также перерисовывается после добавления каждого прямоугольника, но это можно легко изменить способом, аналогичным опубликованному вами коду, чтобы макет перекрашивался только после того, как «партии» прямоугольников были добавлено:

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

Это упрощенный подход, решающий непосредственную проблему обновления пользовательского интерфейса, чтобы показать ход некоторых фоновых изменений, внесенных в этот пользовательский интерфейс с использованием этого очень простого контрольного примера. Является ли это на самом деле наиболее подходящим подходом в вашем конкретном случае, только вы действительно можете сказать.

1
Deltics 22 Мар 2017 в 00:01