我们在程序中经常用到定时器,但是如果你的代码想同时在VCL和FMX中使用,那么由于 VCL 的 TTimer 位于 Vcl.ExtCtrls 单元,而 FMX 的 TTimer 位于 FMX.Types 中,造成我们没有办法简单的创建跨框架的 TTimer 类。
好在,我们还有 RTTI 这个东西,而 TTimer 无论 VCL 还是 FMX 都是基于 TComponent 来的,所以,我们可以使用下面的代码来创建公共的Timer。下面是参考代码,注意引用 System.Rtti 单元。
function CreateTimer(AOwner:TComponent;AInterval:Cardinal;AOnTimer:TNotifyEvent):TComponent;
var
AType:TRttiType;
AContext:TRttiContext;
AValue:TValue;
begin
AContext:=TRttiContext.Create;
AType := AContext.FindType('Vcl.ExtCtrls.TTimer');
if not Assigned(AType) then
begin
AType:=AContext.FindType('FMX.Types.TTimer');
if not Assigned(AType) then
Exit(nil);
end;
if Assigned(AType.Handle.TypeData.ClassType) then
begin
Result := TComponentClass(AType.Handle.TypeData.ClassType).Create(AOwner);
if Assigned(AOnTimer) then
begin
TValue.Make<TNotifyEvent>(AOnTimer,AValue);
AType.GetProperty('OnTimer').SetValue(Result,AValue);
end;
if AInterval<>1000 then
begin
AValue:=AInterval;
AType.GetProperty('Interval').SetValue(Result,AValue);
end;
end;
end;
当然,为了更好用一些,我们做一个简易的封装,引用下 System.Rtti,System.TypInfo 两个单元。
type
TZTimer = record
class function Create(AOwner: TComponent): TComponent; overload; static;
class function Create(AOwner: TComponent; AOnTimer: TNotifyEvent; AInterval: Cardinal = 1000;
AEnabled: Boolean = true): TComponent; overload; static;
class procedure SetEnabled(AOwner: TComponent; AEnabled: Boolean); static;
class function GetEnabled(AOwner: TComponent): Boolean; static;
class procedure SetInterval(AOwner: TComponent; AInterval: Cardinal); static;
class function GetInterval(AOwner: TComponent): Cardinal; static;
class procedure SetOnTimer(AOwner: TComponent; AOnTimer: TNotifyEvent); static;
class function GetOnTimer(AOwner: TComponent): TNotifyEvent; static;
class property Enabled[AOwner: TComponent]: Boolean read GetEnabled write SetEnabled;
class property Interval[AOwner: TComponent]: Cardinal read GetInterval write SetInterval;
class property OnTimer[AOwner: TComponent]: TNotifyEvent read GetOnTimer write SetOnTimer;
end;
{ TZTimer }
class function TZTimer.Create(AOwner: TComponent): TComponent;
var
AType: TRttiType;
AContext: TRttiContext;
begin
AContext := TRttiContext.Create;
AType := AContext.FindType('Vcl.ExtCtrls.TTimer');
if not Assigned(AType) then
begin
AType := AContext.FindType('FMX.Types.TTimer');
if not Assigned(AType) then
Exit(nil);
end;
if Assigned(AType.Handle.TypeData.ClassType) then
Result := TComponentClass(AType.Handle.TypeData.ClassType).Create(AOwner)
else
Result := nil;
end;
class function TZTimer.Create(AOwner: TComponent; AOnTimer: TNotifyEvent; AInterval: Cardinal; AEnabled: Boolean)
: TComponent;
begin
Result := Create(AOwner);
if Assigned(AOnTimer) then
OnTimer[Result] := AOnTimer;
if AInterval <> 1000 then
Interval[Result] := AInterval;
if not AEnabled then
Enabled[Result] := false;
end;
class function TZTimer.GetEnabled(AOwner: TComponent): Boolean;
begin
Result := GetOrdProp(AOwner, GetPropInfo(AOwner, 'Enabled')) <> 0;
end;
class function TZTimer.GetInterval(AOwner: TComponent): Cardinal;
begin
Result := GetOrdProp(AOwner, GetPropInfo(AOwner, 'Interval'));
end;
class function TZTimer.GetOnTimer(AOwner: TComponent): TNotifyEvent;
var
AMethod: TMethod absolute Result;
begin
AMethod := GetMethodProp(AOwner, GetPropInfo(AOwner, 'OnTimer'));
end;
class procedure TZTimer.SetEnabled(AOwner: TComponent; AEnabled: Boolean);
begin
SetOrdProp(AOwner, GetPropInfo(AOwner, 'Enabled'), Ord(AEnabled));
end;
class procedure TZTimer.SetInterval(AOwner: TComponent; AInterval: Cardinal);
begin
SetOrdProp(AOwner, GetPropInfo(AOwner, 'Interval'), AInterval);
end;
class procedure TZTimer.SetOnTimer(AOwner: TComponent; AOnTimer: TNotifyEvent);
var
AMethod: TMethod absolute AOnTimer;
begin
SetMethodProp(AOwner, GetPropInfo(AOwner, 'OnTimer'), AMethod);
end;
现在我们就可直接在VCL/FMX 用同一套代码创建 TTimer 实例了。
var
ATimer:TComponent;
begin
ATimer := TZTimer.Create(Self);
TZTimer.Interval[ATimer] := 100;
TZTimer.OnTimer[ATimer] := DoTimer;
TZTimer.Enabled[ATimer] := true;
end;
当然,我们 TZTimer 还额外封装了一个 Create 的重载,可以直接将上面的代码简化为:
TZTimer.Create(Self,DoTimer,100,true);
