2017-11-06 9 views
0

나는 하루 이틀 동안이 문제에 시달렸으며 아무 데나 대답을 찾을 수 없다. 나는 this answer might help라고 생각했지만 그렇지 않았다.익명 스레드 내에서 TFloatAnimation. 스레드가 죽지 않고 애니메이션이 다시 시작되지 않는다

아래 샘플 코드에는 두 개가 있습니다. Timage 구성 요소 각각에 "시작 이미지"가 들어 있습니다. 두 개의 익명 스레드가 만들어지고 "시작 버튼"을 클릭하면 이미지 1과 이미지 시작 사이에 Image1이 움직이고 다른 하나는 이미지 2에서 동일하게 작동합니다.

내 문제는 KillAnimation 부울 True로 설정하면 모두 애니메이션을 중지해야한다는 것입니다 (그들이하는)하지만 스레드 출구의 하나는, 다른 하나는 애니메이션을 중지하지만, 이미지 중간 애니메이션을 떠난다.

미리 정의 된 스레드를 사용하는 경우에도 동일한 문제가 발생합니다.

샘플 응용 프로그램에는 이미지가 두 개뿐입니다. 실제 응용 프로그램은 15에서 24 사이의 어느 곳에서나 사용할 수 있습니다. 익명 스레드는 만들 수 있고 최대 24 개의 TThread를 정의 할 필요가 없으므로 적합합니다. 나는 그것이 의미가 있기를 바랍니다.

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. 

나는 스레드 내에서 전환 및 FloatAnimation 만들기 FreeOnTerminate이 True이기 때문에 할 때이 파괴 될 것이라고 의미라고 생각했을 것이다.

"image1.bitmap.loadfromfile"을 사용하여 이미지를 재설정하려고 시도하면 변경되지 않습니다.

이미지는 170 x 170png 파일입니다.

내가 여기서 잘못 했습니까?

내 목표는 스레드에 TImage 및 이미지 파일을 전달하고 중지 할 때까지 움직 이도록하는 것입니다. 24 개의 이미지를 모두 애니메이션으로 만들 필요는 없지만 결코 알 수는 없습니다.

샘플에서 모든 코드를 게시해서는 안된다면 사과드립니다. 적어도 당신은 모든 일이 진행되고 있음을 볼 수 있습니다.

+2

첫째, 권장되지 않는 GUI 컴포넌트에 직접 통화를 처리하는 스레드를 사용하여. 또한 스레드에서'Application.HandleMessage'를 호출하고 있는데 이것은 올바르지 않습니다. Wiggle과 TheFloat은 코드에 의해 완성되어야합니다. 나는이 모든 것을 메인 쓰레드에서 작동하도록 만들 것을 제안한다. –

+0

@LURD 메인 UI 스레드 내에서 애니메이션을 시도했지만 버튼이 눌려 질 때까지 반복되는 애니메이션이 4 개 또는 5 개있을 때 버튼 등이 반응하지 않게되었습니다. Transitions 및 FloatAnimate를 비롯한 FMX 구성 요소가 스레드로부터 안전하다고 생각했습니다. 흥미롭게도 application.processMessages가 작동하지 않아 애니메이션이 재생되지 않습니다. 당신의 의견과 생각을 주셔서 감사합니다 :) – Craig

+0

@LURD 나는 또한 2 개의 스레드가 시작되지만 1 개의 끝이라는 사실에 의아해합니다. 코드가 동일하기 때문에 조금 이상합니다. 두 애니메이션은 모두 멈추지 만 스레드는 바람에 매달려 있습니다. – Craig

답변

3

너는 이것을 위해 스레드를 사용하면 안됩니다. 시각적 효과를 비롯한 UI 요소는 기본 UI 스레드에서만 사용해야합니다. 그리고 모바일 플랫폼 용 앱을 개발하지 않는 한, 객체가 범위를 벗어 났을 때 자동으로 파괴되지 않습니다. 객체를 사용할 때 객체를 파괴해야합니다. 그렇지 않으면 객체를 파괴 할 소유자를 할당해야합니다.

대신을 시도해보십시오

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. 
+0

Thanks @Remy. 결국 모바일 앱이 될 것입니다. 나는 원래 메인 UI 내에서이 작업을 수행했지만 다른 버튼은 최대 15 개 이상의 애니메이션이 재생 될 경우 충분히 빠르게 반응하지 않습니다. 슬롯 머신 (내 아내를위한)입니다. 모든 릴은 실 (5 개) 내에서 회전하며 모든 릴은 올바르게 작동하고 종료합니다. 가능한 한 주 UI에서 타일 애니메이션을 좀 더 할거야. 감사합니다. :) – Craig

+0

재미있는 사실 : ResetImages 절차는 처음 실행될 때만 작동합니다. 애니메이션 후 "시작 이미지"는 표시되지 않습니다. 처음 실행될 때 Timages에는 자식이 없습니다. 실행 후에는이를 살펴볼 것입니다. – Craig

+2

작업자 스레드에서 애니메이션 된 시각 효과를 실행하고 기본 UI 스레드와 동기화하지 않고 UI 컨트롤을 조작 할 수 없습니다. 이 효과는 기본 UI 스레드에서만 조작을 수행해야하며,이 경우 작업자 스레드는 쓸모 없게됩니다. 많은 애니메이션 효과가 실행 중이고 사용자 인터페이스가 사용자 상호 작용에 너무 느린 경우 효과가 비효율적이거나 그렇지 않을 때 주 UI 스레드에서 방해를 유발하는 다른 작업을 수행하고 있음을 나타냅니다. . –