内容简介:!错误!在 Android 下这么用 ShowModal 是错误的!
1、直接调用 ShowModal 肯定是不行的,Android 下直接抛出异常。而 iOS、OSX、Windows 下是没问题的。
2、像下面这样用循环模拟 ShowModal 也是不行的,如果只是这么简单,Delphi 早就实现了。这个代码在我手机上实测存在的主要问题就是你按回退键没响应。
Delphi/Pascal
var F:TForm2; begin F:=TForm2.Create(nil); F.Show; while F.Visible and (F.ModalResult=mrNone) do begin Application.ProcessMessages; Sleep(10); end; FreeAndNil(F); end;
var F:TForm2; begin F:=TForm2.Create(nil); F.Show; while F.Visibleand (F.ModalResult=mrNone) do begin Application.ProcessMessages; Sleep(10); end; FreeAndNil(F); end;
3、像下面的用法也是错误的:
Delphi/Pascal
procedure TForm1.Button1Click(Sender:TObject) var dlg: TForm2; begin dlg := TForm2.Create(nil); dlg.ShowModal( procedure(ModalResult: TModalResult) begin if ModalResult = mrOK then if dlg.ListBox1.ItemIndex >= 0 then edit1.Text := dlg.ListBox1.Items [dlg.ListBox1.ItemIndex]; dlg.DisposeOf; end); end;
procedure TForm1.Button1Click(Sender:TObject) var dlg: TForm2; begin dlg := TForm2.Create(nil); dlg.ShowModal( procedure(ModalResult: TModalResult) begin if ModalResult = mrOKthen if dlg.ListBox1.ItemIndex >= 0 then edit1.Text := dlg.ListBox1.Items [dlg.ListBox1.ItemIndex]; dlg.DisposeOf; end); end;
这个的问题在于 dlg 是局部变量,ShowModal 回调的匿名函数里访问Button1Click 里的局部变量是不安全的(栈可能已经错乱)。这块 FMX 的设计真是一个败笔,应该加入实例的地址。当然了,如果 dlg 是一个全局变量,上面的代码就不存啥问题了。
正确的用法:
好吧,得罪了人,批判了别人的不对,总得给出一个对的方法吧。这个方法实际上也说不上真正的对,我暂时称之为对是因为这是目前我能想到的相对完美的解决方案。
Delphi/Pascal
type TFormModalProc = reference to procedure(F: TForm); TFormModalHook = class(TComponent) private FForm: TForm; FCloseAction: TCloseAction; FOldClose: TCloseEvent; FResultProc: TFormModalProc; procedure DoFormClose(Sender: TObject; var Action: TCloseAction); public constructor Create(AOwner: TComponent); override; procedure ShowModal(AResult: TFormModalProc); end; procedure ModalDialog(F: TForm; OnResult: TFormModalProc; ACloseAction: TCloseAction = TCloseAction.caFree); var AHook: TFormModalHook; begin AHook := TFormModalHook.Create(F); AHook.FCloseAction := ACloseAction; AHook.ShowModal(OnResult); end; { TFormModalHook } constructor TFormModalHook.Create(AOwner: TComponent); begin inherited Create(AOwner); FForm := AOwner as TForm; FOldClose := FForm.OnClose; FForm.OnClose := DoFormClose; FCloseAction := TCloseAction.caFree; end; procedure TFormModalHook.DoFormClose(Sender: TObject; var Action: TCloseAction); begin if FForm.ModalResult = mrNone then FForm.ModalResult := mrCancel; Action := FCloseAction; if Assigned(FOldClose) then FOldClose(Sender, Action); end; procedure TFormModalHook.ShowModal(AResult: TFormModalProc); begin FResultProc := AResult; {$IFDEF ANDROID} FForm.ShowModal( procedure(AResult: TModalResult) var AHook: TFormModalHook; AForm: TForm; I: Integer; AChild: TComponent; begin if Screen.ActiveForm is TForm then AForm := Screen.ActiveForm as TForm else begin raise Exception.Create('You should not in here.'); end; if Assigned(AForm) then begin AForm.OnClose := FOldClose; for I := 0 to AForm.ComponentCount - 1 do begin AChild := FForm.Components[I]; if AChild is TFormModalHook then begin (AChild as TFormModalHook).FResultProc(AForm); FreeAndNil(AChild); Break; end; end; end; end); {$ELSE} FForm.ShowModal; FResultProc(FForm); {$ENDIF} end;
type TFormModalProc = referenceto procedure(F: TForm); TFormModalHook = class(TComponent) private FForm: TForm; FCloseAction: TCloseAction; FOldClose: TCloseEvent; FResultProc: TFormModalProc; procedure DoFormClose(Sender: TObject; var Action: TCloseAction); public constructor Create(AOwner: TComponent); override; procedure ShowModal(AResult: TFormModalProc); end; procedure ModalDialog(F: TForm; OnResult: TFormModalProc; ACloseAction: TCloseAction = TCloseAction.caFree); var AHook: TFormModalHook; begin AHook := TFormModalHook.Create(F); AHook.FCloseAction := ACloseAction; AHook.ShowModal(OnResult); end; { TFormModalHook } constructor TFormModalHook.Create(AOwner: TComponent); begin inherited Create(AOwner); FForm := AOwneras TForm; FOldClose := FForm.OnClose; FForm.OnClose := DoFormClose; FCloseAction := TCloseAction.caFree; end; procedure TFormModalHook.DoFormClose(Sender: TObject; var Action: TCloseAction); begin if FForm.ModalResult = mrNonethen FForm.ModalResult := mrCancel; Action := FCloseAction; if Assigned(FOldClose) then FOldClose(Sender, Action); end; procedure TFormModalHook.ShowModal(AResult: TFormModalProc); begin FResultProc := AResult; {$IFDEFANDROID} FForm.ShowModal( procedure(AResult: TModalResult) var AHook: TFormModalHook; AForm: TForm; I: Integer; AChild: TComponent; begin if Screen.ActiveFormis TFormthen AForm := Screen.ActiveFormas TForm else begin raise Exception.Create('You should not in here.'); end; if Assigned(AForm) then begin AForm.OnClose := FOldClose; for I := 0 to AForm.ComponentCount - 1 do begin AChild := FForm.Components[I]; if AChildis TFormModalHookthen begin (AChildas TFormModalHook).FResultProc(AForm); FreeAndNil(AChild); Break; end; end; end; end); {$ELSE} FForm.ShowModal; FResultProc(FForm); {$ENDIF} end;
好吧,代码看起来有点多,多就多吧。用法很简单,用它替换 TForm.ShowModal 方法,如:
Delphi/Pascal
procedure TForm1.Button1Click(Sender: TObject); var F: TForm2; begin F := TForm2.Create(nil); ModalDialog(F, procedure(AForm: TForm) begin ShowMessage(AForm.Name + ' Modal result ready'); end); end;
procedure TForm1.Button1Click(Sender: TObject); var F: TForm2; begin F := TForm2.Create(nil); ModalDialog(F, procedure(AForm: TForm) begin ShowMessage(AForm.Name + ' Modal result ready'); end); end;
【注意】
不要在 ModalDialog 的回调函数中,在可能引发消息循环处理的地方,如:ShowMessage/MessageDlg/ProcessMessages 等函数的后面再引用 AForm 的地址,因为在 FMX 框架下,它很可能会被释放掉了。
以上所述就是小编给大家介绍的《!错误!在 Android 下这么用 ShowModal 是错误的!》,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对 码农网 的支持!
猜你喜欢:- Golang学习笔记之错误处理error、panic (抛出错误),recover(捕获错误)
- c – 构建PBRT v2错误 – 错误1错误U1077:’if’:返回代码’0x1′
- Google开源ClusterFuzz:使得查找错误并修复错误变得异常简单
- 脚本错误量极致优化-定位压缩且无 SourceMap 文件的脚本错误
- php – 解析错误:语法错误,意外’未设置'(T_UNSET)
- 可以让程序告诉我详细的页面错误和数据库连接错误吗?
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。