【问题标题】:FireMonkey controls do not animate smoothlyFireMonkey 控件动画不流畅
【发布时间】:2012-01-14 17:13:57
【问题描述】:

背景

我使用一些 FireMonkey 控件创建了一个 GUI。

  • 一些控件是动画的,它们的外观会自动更新。
  • 某些控件仅根据用户交互(滑块等)进行更新。

问题

与用户控件的交互会阻止更新动画控件,从而导致动画不连续。

Video of glitchy animation

上面视频中的动画控件由 TTimer 组件驱动。使用 FireMonkey 的动画组件时问题仍然存在。

调查

滑块控件在调整时调用 Repaint()。平滑调整滑块将生成密集的 Repaint() 调用流,从而阻止更新其他控件。

怎么办?

在一个控件不断更新时冻结动画不适合我的应用程序。我的第一个想法是将 Repaint() 调用换成类似于 VCL Invalidate() 方法的东西,但是 FireMonkey 没有任何可比的 AFAIK。

这个问题有没有好的解决方法?

【问题讨论】:

  • 你试过用一些 TAnimation 后代替换 TTimer 吗?
  • @Torbins:是的,我尝试使用 TFloatAnimation 组件。情况再好不过了。查看 FMX 源代码,TAnimation 组件也由全局 TTimer 对象触发。
  • 那么也许是一个单独的线程?但我不确定 FireMonkey 是否是线程安全的。可能不会。
  • 所以剩下的唯一一件事是:等到他们在许多其他问题中解决这个问题。有些人甚至将 FireMonkey 称为“技术预览”。
  • @Torbins FireMonkey 代码不受关键部分 AFAIK 的保护,所以我怀疑它不是线程安全的。就像 VCL 一样。覆盖 RePaint 并使用您自己的基于计时器的专用方法怎么样?

标签: delphi firemonkey


【解决方案1】:

正如 Arnaud Bouchez 在上面的 cmets 中建议的那样,我创建了一个基于计时器的重绘方法。到目前为止,它似乎工作。

代码

unit FmxInvalidateHack;

interface

uses
  Fmx.Types;

procedure InvalidateControl(aControl : TControl);


implementation

uses
  Contnrs;

type
  TInvalidator = class
  private
  protected
    Timer : TTimer;
    List  : TObjectList;
    procedure Step(Sender : TObject);
  public
    constructor Create;
    destructor Destroy; override;

    procedure AddToQueue(aControl : TControl);
  end;

var
  GlobalInvalidator : TInvalidator;

procedure InvalidateControl(aControl : TControl);
begin
  if not assigned(GlobalInvalidator) then
  begin
    GlobalInvalidator := TInvalidator.Create;
  end;
  GlobalInvalidator.AddToQueue(aControl);
end;


{ TInvalidator }

constructor TInvalidator.Create;
const
  FrameRate = 30;
begin
  List  := TObjectList.Create;
  List.OwnsObjects := false;

  Timer := TTimer.Create(nil);
  Timer.OnTimer  := Step;
  Timer.Interval := round(1000 / FrameRate);
  Timer.Enabled  := true;
end;

destructor TInvalidator.Destroy;
begin
  Timer.Free;
  List.Free;
  inherited;
end;

procedure TInvalidator.AddToQueue(aControl: TControl);
begin
  if List.IndexOf(aControl) = -1 then
  begin
    List.Add(aControl);
  end;
end;

procedure TInvalidator.Step(Sender: TObject);
var
  c1: Integer;
begin
  for c1 := 0 to List.Count-1 do
  begin
    (List[c1] as TControl).Repaint;
  end;
  List.Clear;
end;


initialization

finalization
  if assigned(GlobalInvalidator) then GlobalInvalidator.Free;

end.

==

用法

一个控件可以通过调用来重绘:

InvalidateControl(MyControl);

InvalidateControl() 过程不会立即重新绘制控件。相反,它将控件添加到列表中。全局计时器稍后检查列表,调用 Repaint() 并从列表中删除控件。使用此方法,可以根据需要使控件无效,但不会像快速 Repaint() 调用那样阻止其他控件的更新。

【讨论】:

    猜你喜欢
    • 2014-10-30
    • 2010-11-07
    • 2017-07-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-05-17
    • 2018-07-28
    • 2015-11-28
    相关资源
    最近更新 更多