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

查看所有标签

猜你喜欢:

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

大数据大创新:阿里巴巴云上数据中台之道

大数据大创新:阿里巴巴云上数据中台之道

邓中华 / 电子工业出版社 / 2018-11 / 99

阿里巴巴云上数据中台正服务着阿里生态中的数十个业务板块、百余家公司、千万级客户,在帮助决策层看清甚至决定业态走向的同时,在上万个业务场景中应用并催生创新。 《大数据大创新:阿里巴巴云上数据中台之道》基于作者在阿里巴巴的十年大数据从业经历,精彩演绎云上数据中台之道。《大数据大创新:阿里巴巴云上数据中台之道》基于大数据探索的大趋势,讲述阿里巴巴云上数据中台顶层设计,再以实际案例详述阿里巴巴云上数......一起来看看 《大数据大创新:阿里巴巴云上数据中台之道》 这本书的介绍吧!

RGB转16进制工具
RGB转16进制工具

RGB HEX 互转工具

正则表达式在线测试
正则表达式在线测试

正则表达式在线测试

HEX HSV 转换工具
HEX HSV 转换工具

HEX HSV 互换工具