匿名线程中的TFloatAnimation。线程不会死,动画也不会重启

时间:2017-11-06 06:00:56

标签: multithreading delphi animation delphi-10.2-tokyo

我一直在努力争取一两天,无法在任何地方找到答案。我想this answer might help但事实并非如此。

在我的示例代码中,我有两个 Timage 组件,每个组件都包含一个“开始图像”。单击“开始按钮”时,会创建两个匿名线程,一个在“开始图像”和“结束图像”之间激活Image1,另一个对 Image2 执行相同操作。

我的问题是,当 KillAnimation 布尔值设置为True时,两个动画应该停止(他们这样做),但只有一个线程退出,另一个停止动画但是让图像保持在动画。

如果我也使用预定义的线程,我也会遇到同样的问题。

示例应用程序只有两个图像,真实世界的应用程序可以有15到24之间的任何地方。匿名线程似乎适合,因为我可以创建它们,而不必担心定义多达24个TThreads。我希望这是有道理的。

unit Unit1;

interface

uses
  System.SysUtils, System.Types, System.UITypes, System.Classes,
  System.Variants,
  FMX.Types, FMX.Controls, FMX.Forms, FMX.Graphics,  FMX.Objects,
  FMX.Controls.Presentation, FMX.StdCtrls, FMX.Ani, FMX.Effects,
  FMX.Filter.Effects;

type
  TForm1 = class(TForm)
    Image1: TImage;
    Image2: TImage;
    BtnStop: TButton;
    BtnStart: TButton;
    BtnReset: TButton;
    procedure BtnStopClick(Sender: TObject);
    procedure BtnStartClick(Sender: TObject);
    procedure BtnResetClick(Sender: TObject);
    procedure FormCreate(Sender: TObject);
  private
    { Private declarations }
  public
    { Public declarations }
    procedure createthread(TheImage: TImage; TargetBMP: String);
  end;

var
  Form1: TForm1;
  KillAnimation: Boolean;

implementation

{$R *.fmx}

procedure TForm1.BtnStopClick(Sender: TObject);
begin
  KillAnimation := true;
end;

procedure TForm1.createthread(TheImage: TImage; TargetBMP: String);
begin
  { The thread works right up until it is stopped. Even though two
    threads are started only one finishes. In addition, the images
    do not end up as the target image, and neither image can be
    reset even if I reload them from files. }

  TThread.CreateAnonymousThread(
    procedure()
    var
      Wiggle: TWiggleTransitionEffect;
      TheFloat: TFloatAnimation;
    begin
      TThread.NameThreadForDebugging('Animate ' + TheImage.Name);
      Wiggle := TWiggleTransitionEffect.Create(Nil);
      Wiggle.RandomSeed := 0.3;
      Wiggle.Progress := 0;
      Wiggle.Parent := TheImage;

      TThread.Synchronize(TThread.CurrentThread,
        procedure()
        Begin
          Wiggle.Target.LoadFromFile(TargetBMP)
        end);

      TheFloat := TFloatAnimation.Create(Nil);
      TheFloat.Parent := Wiggle;
      TheFloat.PropertyName := 'Progress';
      TheFloat.Duration := 2;
      TheFloat.AutoReverse := true;
      TheFloat.Loop := true;
      TheFloat.StartValue := 0;
      TheFloat.StopValue := 100;
      TheFloat.StartFromCurrent := false;
      TheFloat.start;

      while not KillAnimation do
        application.handlemessage;

      TheFloat.stop;
    end).start;
end;

procedure TForm1.FormCreate(Sender: TObject);
begin

  Image1.Bitmap.LoadFromFile('c:\sample\startimage.png');
  Image2.Bitmap.LoadFromFile('c:\sample\startimage.png');

end;

procedure TForm1.BtnStartClick(Sender: TObject);
begin
  KillAnimation := false;
  createthread(Image1, 'c:\sample\endimage.png');
  createthread(Image2, 'c:\sample\endimage.png');

end;

procedure TForm1.BtnResetClick(Sender: TObject);
begin
  KillAnimation := false;
  Image1.Bitmap.LoadFromFile('c:\sample\startimage.png');
  Image2.Bitmap.LoadFromFile('c:\sample\startimage.png');
end;

end.

我原以为在线程中创建Transition和FloatAnimation意味着它们会在完成时被销毁,因为FreeOnTerminate是True。

当我尝试使用“image1.bitmap.loadfromfile”重置图像时,它不会改变。

图像为170 x 170 png文件。

我在这里做错了什么?

我的目标是将TImage和一个图像文件传递给线程,让它动画直到被告知停止。我不太可能需要所有24张图像来制作动画,但你永远不会知道。

道歉,如果我不应该发布我的样本中的所有代码。至少你可以看到一切正在发生。

1 个答案:

答案 0 :(得分:3)

你根本不应该使用线程。 UI元素(包括视觉效果)应仅在主UI线程中使用。除非您正在为移动平台开发应用程序,否则对象在超出范围时不会自动销毁,您必须在使用它们时自行销毁它们,或者为它们分配一个将为您销毁它们的所有者。 / p>

请改为尝试:

unit Unit1;

interface

uses
  System.SysUtils, System.Types, System.UITypes, System.Classes, System.Variants,
  FMX.Types, FMX.Controls, FMX.Forms, FMX.Graphics, FMX.Objects, FMX.Controls.Presentation,
  FMX.StdCtrls, FMX.Ani, FMX.Effects, FMX.Filter.Effects;

type
  TForm1 = class(TForm)
    Image1: TImage;
    Image2: TImage;
    BtnStop: TButton;
    BtnStart: TButton;
    BtnReset: TButton;
    procedure BtnStopClick(Sender: TObject);
    procedure BtnStartClick(Sender: TObject);
    procedure BtnResetClick(Sender: TObject);
    procedure FormCreate(Sender: TObject);
  private
    { Private declarations }
    Float1: TFloatAnimation;
    Float2: TFloatAnimation;
    function PrepareEffect(TheImage: TImage; const TargetBMP: String): TFloatAnimation;
    procedure ResetImages;
  public
    { Public declarations }
  end;

var
  Form1: TForm1;

implementation

{$R *.fmx}

procedure TForm1.BtnResetClick(Sender: TObject);
begin
  ResetImages;
end;

procedure TForm1.BtnStartClick(Sender: TObject);
begin
  Float1.start;
  Float2.start;
end;

procedure TForm1.BtnStopClick(Sender: TObject);
begin
  Float1.stop;
  Float2.stop;
end;

procedure TForm1.FormCreate(Sender: TObject);
begin
  ResetImages;
  Float1 := PrepareEffect(Image1, 'c:\sample\endimage.png');
  Float2 := PrepareEffect(Image2, 'c:\sample\endimage.png');
end;

function TForm1.PrepareEffect(TheImage: TImage; const TargetBMP: String): TFloatAnimation;
var
  Wiggle: TWiggleTransitionEffect;
  TheFloat: TFloatAnimation;
begin
  Wiggle := TWiggleTransitionEffect.Create(Self);
  Wiggle.RandomSeed := 0.3;
  Wiggle.Progress := 0;
  Wiggle.Parent := TheImage;
Wiggle.Target.LoadFromFile(TargetBMP);

  TheFloat := TFloatAnimation.Create(Self);
  TheFloat.Parent := Wiggle;
  TheFloat.PropertyName := 'Progress';
  TheFloat.Duration := 2;
  TheFloat.AutoReverse := true;
  TheFloat.Loop := true;
  TheFloat.StartValue := 0;
  TheFloat.StopValue := 100;
  TheFloat.StartFromCurrent := false;

  Result := TheFloat;
end;

procedure TForm1.ResetImages;
begin   
  Image1.Bitmap.LoadFromFile('c:\sample\startimage.png');
  Image2.Bitmap.LoadFromFile('c:\sample\startimage.png');
end;

end.