!错误!在 Android 下这么用 ShowModal 是错误的!

栏目: 编程语言 · 发布时间: 6年前

内容简介:!错误!在 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 是错误的!》,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对 码农网 的支持!

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

Head First Python

Head First Python

Paul Barry / O'Reilly Media / 2010-11-30 / USD 49.99

Are you keen to add Python to your programming skills? Learn quickly and have some fun at the same time with Head First Python. This book takes you beyond typical how-to manuals with engaging images, ......一起来看看 《Head First Python》 这本书的介绍吧!

JSON 在线解析
JSON 在线解析

在线 JSON 格式化工具

HTML 编码/解码
HTML 编码/解码

HTML 编码/解码

MD5 加密
MD5 加密

MD5 加密工具