在XE8 Update1下试用,发现在Win32下正常,而到了Android下则不正常。
原文作者的实现思路值得赞赏,利用Helper的方式,为Form增加了两个方法:
TSaveStateHelper = class helper for TCustomForm
public const
DATA_FIELD = 'data';
procedure SaveFormState;//保存窗口状态
procedure LoadFormState;//加载窗口状态
end;
然后,利用XE8支持的JSON对象,将Form上的控件值保存到Form.SaveState.Stream中:
procedure TSaveStateHelper.SaveFormState;
var
FormJSONObject: TJSONObject;
I: Integer;
FMXObj: TFmxObject;
FMXJObj: TJSONObject;
W: TBinaryWriter;
begin
try
FormJSONObject := TJSONObject.Create;
try
for I := 0 to Self.ComponentCount - 1 do
begin
if not(Self.Components[I] is TFmxObject) then
continue;
FMXObj := Self.Components[I] as TFmxObject;
FMXJObj := TJSONObject.Create;
case FMXObj.Data.Kind of
tkUnknown:
;
tkInteger:
FMXJObj.AddPair(DATA_FIELD,
TJSONNumber.Create(FMXObj.Data.AsInteger));
tkChar:
;
tkEnumeration:
if FMXObj.Data.AsBoolean then
FMXJObj.AddPair(DATA_FIELD, TJSONTrue.Create)
else
FMXJObj.AddPair(DATA_FIELD, TJSONFalse.Create);
tkFloat:
FMXJObj.AddPair(DATA_FIELD,
TJSONNumber.Create(FMXObj.Data.AsExtended));
tkString, tkUString, tkLString, tkWString:
FMXJObj.AddPair(DATA_FIELD, FMXObj.Data.AsString);
tkSet:
;
tkClass:
;
tkMethod:
;
tkWChar:
;
tkVariant:
;
tkArray:
;
tkRecord:
;
tkInterface:
;
tkInt64:
FMXJObj.AddPair(DATA_FIELD,
TJSONNumber.Create(FMXObj.Data.AsInt64));
tkDynArray:
;
tkClassRef:
;
tkPointer:
;
tkProcedure:
;
end;
FormJSONObject.AddPair(FMXObj.Name, FMXJObj)
end;
SaveState.Stream.Clear;
W := TBinaryWriter.Create(SaveState.Stream);
try
W.Write(FormJSONObject.ToJSON);
finally
W.Free;
end;
finally
FormJSONObject.Free;
end;
except
on e: Exception do
Log.d('SaveStateHelper', Self, e.Message);
end;
end;
上面的代码,只是简单读取窗口上的控件,如果控件放到TLayout等布局控件中,或者使用了Frame,那则不能保存,这一点要注意,当然,可以进一步完美代码。
我们再看看作者如何加载保存过的内容到窗口控件:
procedure TSaveStateHelper.LoadFormState;
var
R: TBinaryReader;
FormJSONObject: TJSONObject;
I: Integer;
FMXObj: TFmxObject;
FMXJObj: TJSONObject;
s: string;
begin
try
if not(SaveState.Stream.Size > 0) then
exit;
// Recover previously typed values in all control.
R := TBinaryReader.Create(SaveState.Stream);
try
FormJSONObject := TJSONObject.ParseJSONValue(R.ReadString) as TJSONObject;
try
for I := 0 to Self.ComponentCount - 1 do
begin
if not(Self.Components[I] is TFmxObject) then
continue;
FMXObj := Self.Components[I] as TFmxObject;
FMXJObj := FormJSONObject.Values[FMXObj.Name] as TJSONObject;
if FMXJObj = nil then//这里,我加了判断
continue;
if FMXJObj.Values[DATA_FIELD] <> nil then//这里,我加了判断
continue;
case FMXObj.Data.Kind of
tkUnknown:
;
tkInteger:
FMXObj.Data :=
(FMXJObj.GetValue(DATA_FIELD) as TJSONNumber).AsInt;
tkChar:
;
tkEnumeration:
if FMXJObj.GetValue(DATA_FIELD) is TJSONTrue then
FMXObj.Data := true
else
FMXObj.Data := false;
tkFloat:
FMXObj.Data :=
(FMXJObj.GetValue(DATA_FIELD) as TJSONNumber).AsDouble;
tkString, tkUString, tkLString, tkWString:
FMXObj.Data :=
(FMXJObj.GetValue(DATA_FIELD) as TJSONString).Value;
tkSet:
;
tkClass:
;
tkMethod:
;
tkWChar:
;
tkVariant:
;
tkArray:
;
tkRecord:
;
tkInterface:
;
tkInt64:
FMXObj.Data :=
(FMXJObj.GetValue(DATA_FIELD) as TJSONNumber).AsInt64;
tkDynArray:
;
tkClassRef:
;
tkPointer:
;
tkProcedure:
;
end;
end;
finally
FormJSONObject.Free;
end;
finally
R.Free;
end;
except
on e: Exception do
Log.d('SaveStateHelper', Self, e.Message);
end;
end;
关于SaveState对象,是TForm的一个属性,是Delphi实现的用来保存窗口的状态,当使用SaveState.Stream时,会自动读取 前期保存过的内容到SaveState.Stream中,即当我们使用SaveState.Stream进,Stream会自动加载。
通过上面的代码,我们很清晰的看到如何操作SaveState.Stream这个对象,值得学习。
由于在Android下不能正常运行,通过跟踪delphi的实现,发现Delphi在Android下存在bug,暂时我也没清楚,不过,找到暂时避开这个bug的方法,那就是设置保存内容的路径,下面代码红色部分是设置SaveState的路径,测试,在Win32及Android通过。
procedure TForm1.FormCreate(Sender: TObject);
begin
if TPlatformServices.Current.SupportsPlatformService(IFMXSaveStateService, SaveStateService) then
SaveStateService.SetStoragePath(System.IOUtils.TPath.GetDocumentsPath);
LoadFormState;
end;
通过跟踪关于Delphi对Form状态的管理的实现,能够理解,原来Delphi是想在我们不设置这个路径的情况下,只是操作SateState对象,也能正常工作,即最大的简化开发者的代码量,只可惜在Android上没实现好。(没有在Xe7下试是否正常工作,只是在XE8 update1测试)
此外,在android下,如果直接在MainForm上直接退出应用,Form.OnSaveState事件,没有触发,如果在这个事件中保存Form状态,则不能保存。如果用Home返回应用,再进应用,这个事件工作正常。
另,没有取得原文作者的同意,如果侵权,可留言告之,我会删除此文。
评论