728x90

출처 : FMXExpress/android-object-pascal-wrapper
Android JNI Bridge and Custom Classes.dex
Java2OP.exe, the Native Bridge File Generator for Android
파이어몽키에서 안드로이드 외부 라이브러리(jar 파일) 이용(Import jar)
델파이 파이어몽키 안드로이드 앱 내에 스플래시 스크린추가
Creating a splash screen for Delphi XE6 Android apps
Delphi XE6のAndroidアプリケ?ションで、Intentを使ってアクティビティを開始して?り値を取得するサンプル
android-file-dialog

안드로이드 TOpenDialog 구현

FireMonkey의 Android에서 TOpenDialog가 동작하지 않아 android-file-dialog를 이용해서 TOpenDialog처럼 파일를 선택 할 수 있도록 구현 해보았습니다.

android-file-dialog 파일을 받아서 컴파일 해서 jar로 묶고 FireMonkey 프로젝트를 만들고 jar를 추가했습니다.

XE7부터는 classes.dex로 만들지 않고, jar 형태의 라이브러리를 바로 추가 할수 있고, java2op 유틸리티로 브릿지 파일을 생성 할 수 있어서 편하게 안드로이드용 라이브러리 추가가 가능합니다.

안드로이드 브릿지 파일 생성

유틸리티 java2op로 안드로이드 브릿지 파일(Androidapi.JNI.Interfaces.pas)를 생성 합니다.

java2op -jar lib_fileexplorer.jar

라이브러리 jar 추가

   

AndroidManifest.template.xml 파일 수정

<!-- bluexmas -->
<activity android:name="com.lamerman.FileDialog"
          android:configChanges="orientation|keyboard" />

리소스 추가

메뉴 > Project > Deployment 선택

Activity 화면 전환

  Intent := TJIntent.JavaClass.init;
  Intent.setClassName(SharedActivityContext, StringToJString('com.lamerman.FileDialog'));
  SharedActivity.startActivityForResult(Intent, 0);

결과 메시지 받기

HandleActivityMessage 함수로 FileDialog Activity의 결과를 받아 오도록 등록

  FMessageSubscriptionID := TMessageManager.DefaultManager.SubscribeToMessage
    (TMessageResultNotification, HandleActivityMessage);

결과 처리

filename := JStringToString(Data.getStringExtra(StringToJString('RESULT_PATH')));
Edit1.Text := filename;

델파이 전체소스

unit Unit1;

interface

uses
  System.SysUtils, System.Types, System.UITypes, System.Classes, System.Variants,
  FMX.Types, FMX.Controls, FMX.Forms, FMX.Graphics, FMX.Dialogs, FMX.StdCtrls,
  Androidapi.JNI.Interfaces, System.Messaging,
  FMX.Platform.Android,
  Androidapi.Helpers, Androidapi.JNI.App, Androidapi.JNI.GraphicsContentViewText;

type
  TForm1 = class(TForm)
    Button1: TButton;
    procedure Button1Click(Sender: TObject);
  private
    const ScanRequestCode = 0;
    var FMessageSubscriptionID: Integer;
    procedure HandleActivityMessage(const Sender: TObject; const M: TMessage);
    function OnActivityResult(RequestCode, ResultCode: Integer; Data: JIntent): Boolean;
  private
    { Private declarations }
  public
    { Public declarations }
  end;

var
  Form1: TForm1;

implementation

{$R *.fmx}

procedure TForm1.HandleActivityMessage(const Sender: TObject; const M: TMessage);
begin
  if M is TMessageResultNotification then
    OnActivityResult(TMessageResultNotification(M).RequestCode, TMessageResultNotification(M).ResultCode,
      TMessageResultNotification(M).Value);
end;

function TForm1.OnActivityResult(RequestCode, ResultCode: Integer; Data: JIntent): Boolean;
var
  filename : string;
begin
  Result := False;

  TMessageManager.DefaultManager.Unsubscribe(TMessageResultNotification, FMessageSubscriptionID);
  FMessageSubscriptionID := 0;

  // For more info see https://github.com/zxing/zxing/wiki/Scanning-Via-Intent
  if RequestCode = ScanRequestCode then
  begin
    if ResultCode = TJActivity.JavaClass.RESULT_OK then
    begin
      if Assigned(Data) then
      begin
        filename := JStringToString(Data.getStringExtra(StringToJString('RESULT_PATH')));

        Edit1.Text := filename;

        //Toast(Format('Found %s format barcode:'#10'%s', [ScanFormat, ScanContent]), LongToast);
      end;
    end
    else if ResultCode = TJActivity.JavaClass.RESULT_CANCELED then
    begin
      //Toast('You cancelled the scan', ShortToast);
    end;
    Result := True;
  end;
end;

procedure TForm1.Button1Click(Sender: TObject);
var
  Intent: JIntent; // JFileDialog;
begin
  FMessageSubscriptionID := TMessageManager.DefaultManager.SubscribeToMessage
    (TMessageResultNotification, HandleActivityMessage);

  Intent := TJIntent.JavaClass.init;
  Intent.setClassName(SharedActivityContext, StringToJString('com.lamerman.FileDialog'));
  SharedActivity.startActivityForResult(Intent, 0);
end;

end.

실행 

  

  

전체소스

FileExplorer.zip

 

728x90
728x90

출처 : Delphi Firemonkey 개발자 모임

TestPG.7z

Code

unit Unit1;

interface

uses
  System.SysUtils, System.Types, System.UITypes, System.Classes, System.Variants,
  FMX.Types, FMX.Controls, FMX.Forms, FMX.Graphics, FMX.Dialogs, FMX.Objects;

type
  TForm1 = class(TForm)
    Image1: TImage;
    procedure Image1MouseDown(Sender: TObject; Button: TMouseButton;
      Shift: TShiftState; X, Y: Single);
    procedure Image1MouseMove(Sender: TObject; Shift: TShiftState; X,
      Y: Single);
    procedure Image1MouseUp(Sender: TObject; Button: TMouseButton;
      Shift: TShiftState; X, Y: Single);
    procedure FormShow(Sender: TObject);
  private
    { Private declarations }
    FDownPos: TPointF;
    AMouseDownCheck: Boolean;
  public
    { Public declarations }
  end;

var
  Form1: TForm1;

implementation

uses
  FMX.Platform;

{$R *.fmx}

procedure TForm1.FormShow(Sender: TObject);
var
  ScreenScale: Single;
  ScreenSvc: IFMXScreenService;
begin
  if TPlatformServices.Current.SupportsPlatformService(IFMXScreenService, IInterface(ScreenSvc)) then
  begin
    ScreenScale := ScreenSvc.GetScreenScale;
  end;

  Image1.Bitmap.Width := Round(Self.Width * ScreenScale);
  Image1.Bitmap.Height := Round(Self.Height * ScreenScale);
  Image1.Bitmap.Clear(TAlphaColorRec.White);
end;

procedure TForm1.Image1MouseDown(Sender: TObject; Button: TMouseButton;
  Shift: TShiftState; X, Y: Single);
begin
  FDownPos := PointF(X, Y);
  AMouseDownCheck := True;
end;

procedure TForm1.Image1MouseMove(Sender: TObject; Shift: TShiftState; X,
  Y: Single);
begin
  if AMouseDownCheck then
  begin
    with Image1.Bitmap.Canvas do
    begin
      BeginScene();
      Stroke.Color := TAlphaColors.Black;
      StrokeThickness := 2;
      DrawLine(FDownPos, PointF(X, Y), 1);
      EndScene;
    end;
    FDownPos := PointF(X, Y);
  end;
end;

procedure TForm1.Image1MouseUp(Sender: TObject; Button: TMouseButton;
  Shift: TShiftState; X, Y: Single);
begin
  AMouseDownCheck := False;
end;

end.

 

728x90
728x90

출처 : UltraStar Deluxe
Flamefire / Usdx
Re: XE3 + FireMonkeyでScanlineプロパティが消えた?
Bitmap의 Pixel에 접근(TBitmap.Map, TBitmapData)
FFmpeg으로 미디어 스트림 열기 (ver 0.1)
千 Line 으로 비디오플레이어 만들기 1
네이버 건강 서비스 - hello world NHN 개발자 블로그
FMX.Types.TBitmap.Unmap

FireMonkey에서 FFMpeg를 이용해서 이미지 챕쳐 하는 부분을 구현했습니다.
기존에 데이터는 Bitmap의 픽셀단위로 복사해서 구현했는데,
TImageControl 컴포넌트의 Bitamp.TBitmapData에 바로 데이터를 쓰도록 하는 방법을 찾아서,
기존 코드에 불필요한 부분을 제거하고 다시 구현했습니다.

FireMonkey에서 FFMpeg 사용하는 부분도 간단하게 정리했습니다..

파일 포맷과 코덱을 라이브러리 등록

av_register_all();

동영상 파일 읽기

errnum := avformat_open_input(@fFormatContext, Edit1.Text, nil, nil);

스트림 정보 조회

errnum := avformat_find_stream_info(fFormatContext, nil);

비디오, 오디오 스트림 Index 찾기

  for i := 0 to fFormatContext.nb_streams-1 do
  begin
    Stream := PPAVStream(PtrUInt(fFormatContext.streams) + i * Sizeof(pointer))^;

    if (Stream.codec.codec_type = CODEC_TYPE_VIDEO) and
       (FirstVideoStream < 0) then
    begin
      FirstVideoStream := i;
    end;

    if (Stream.codec.codec_type = CODEC_TYPE_AUDIO) and
       (FirstAudioStream < 0) then
    begin
      FirstAudioStream := i;
    end;
  end;

Codec 찾기

fCodec := avcodec_find_decoder(fCodecContext^.codec_id);

Codec 열기

errnum := avcodec_open2(fCodecContext, fCodec, nil);

동영상 정보 출력

  Memo1.Lines.Add('Found a matching Codec: '+ fCodecContext^.Codec.Name + sLineBreak +
    sLineBreak +
    '  Width = '+inttostr(fCodecContext^.width) +
    ', Height='+inttostr(fCodecContext^.height) + sLineBreak +
    '  Aspect    : '+inttostr(fCodecContext^.sample_aspect_ratio.num) + '/' +
                     inttostr(fCodecContext^.sample_aspect_ratio.den) + sLineBreak +
    '  Framerate : '+inttostr(fCodecContext^.time_base.num) + '/' +
                     inttostr(fCodecContext^.time_base.den));

Bitmap 객체 생성, TBitmapData

    ImageControl1.Bitmap := TBitmap.Create(fCodecContext^.width, fCodecContext^.Height);
    ImageControl1.Bitmap.Map(TMapAccess.maWrite, FBmpData);

AVPicture 구조체에 TImageControl.Bitmap의 버퍼와 연결

  errnum := avpicture_fill(PAVPicture(fAVFrameRGB), FBmpData.Data, PIXEL_FMT_FFMPEG,
      fCodecContext^.width, fCodecContext^.height);

파일에서 데이터를 읽기

errnum := av_read_frame(fFormatContext, AVPacket);

디코딩된 YUV 데이터 얻기

avcodec_decode_video2(fCodecContext, fAVFrame, frameFinished, @AVPacket);

색상 공간 변환

errnum := sws_scale(fSwScaleContext, @fAVFrame.data, @fAVFrame.linesize,
                0, fCodecContext^.Height,
                @fAVFrameRGB.data, @fAVFrameRGB.linesize);

Finalizes the customizing session of the current bitmap.

ImageControl1.Bitmap.Unmap(FBmpData);

Mac

Windows

Android

 

728x90
728x90

출처 : Firemonkey Styles - Felix John COLIBRI.
Working with Native and Custom FireMonkey Styles
Step 3 - Add Style-Resources as RCDATA (Delphi)
동영상 - FireMonkey - Style lookup& costom listbox item

style

  object TLayout
    StyleName = 'Layout1'
    DesignVisible = False
    Height = 32.000000000000000000
    Position.X = 499.000000000000000000
    Position.Y = 196.000000000000000000
    Width = 155.000000000000000000
    object TButton
      StyleName = 'Button1'
      Align = alRight
      Height = 32.000000000000000000
      Position.X = 75.000000000000000000
      TabOrder = 1
      Width = 80.000000000000000000
    end
    object TActiveStyleTextObject
      StyleName = 'text'
      Align = alClient
      Color = claBlack
      Height = 17.000000000000000000
      HorzTextAlign = taLeading
      Margins.Left = 3.000000000000000000
      Margins.Top = 1.000000000000000000
      Margins.Right = 3.000000000000000000
      Margins.Bottom = 1.000000000000000000
      Width = 190.000000000000000000
      WordWrap = False
      Shadow.Color = claNull
      ShadowVisible = False
      ActiveTrigger = stSelected
      ActiveColor = claBlack
      ActiveShadow.Color = claNull
    end
  end

예제

procedure TForm1.FormCreate(Sender: TObject);
var
  _Item : TListBoxItem;
begin
  Self.OnGetMoveControl := GetMoveControl;

  _Item := TListBoxItem.Create(nil);
  _Item.Parent := ListBox1;
  _Item.StyleLookup := 'layout1';
  _Item.Text := 'a';
  (_Item.FindStyleResource('button1') as TButton).Text := 'test';
  (_Item.FindStyleResource('button1') as TButton).OnClick := ItemButtonClick;
end;

procedure TForm1.ItemButtonClick(Sender: TObject);
var
  VP: TPointF;
begin
  VP := (Sender as TButton).LocalToAbsolute((Sender as TButton).Position.Point);
  VP := (Sender as TButton).Scene.LocalToScreen(VP);
  {
  VP := (Sender as TButton).Position.Point;
  VP := (Sender as TButton).Scene.LocalToScreen(VP);
  }
  PopupMenu1.Popup(50, 50); // 모바일에서는 동작하지 않음

  ComboBox1.DropDown;
end;
728x90
728x90

출처 : FMXTCanvasDrawFunctions (Delphi)
Fastest way to draw pixels in FireMonkey
Firemonkey Rotate Text
Delphi XE4 FireMonkey/iOS DrawBitmap not working
FIREMONKEY에서 썸네일을 만들려면 (1)
[FMX,VCL 비교] #1 VCL의 Canvas와 FMX의 Canvas 차이
[FM] TCanvasGdiPlus 문제점

FireMonkey - FFMpeg Player

ffmpeg 컴파일 하여 Delphi FireMonkey를 이용해서 Android와 IOS용 Player를 만들어 보았습니다.

처음 XE5가 나오고 일주일 정도 예상했는데, 한달이나 걸렸네요.

처음에는 소스를 공개 할 생각으로 작업했는데, 이것 때문에 너무 고생하다보니, 나중에 강좌를 통해서
강좌를 듣는 분들에게 제공 하도록 하겠습니다.

Android용 Player를 만들어 본적이 있어, JNI 방식으로 Player로 시작했는데,
Delphi는 GCC과 같은 방식으로 호출하는 것이라 JNI 인터페이스로 구현 할 필요가 없더군요.

apk에 정적 라이브러리(static library - .so)를 같이 배포해서 동적로딩 하려고 했었는데,
절대 경로로 로딩은 되는데, apk에 존재하는 정적 라이브러리는 로딩안되더군요.

지금까지 결론은 안드로이드나 IOS는 공유 라이브러리(Shared Library)롤 만들고 컴파일시
링크(ld) 되어야 하는것으로 확인 되었습니다.

IOS의 경우 시뮬레이터에서는 정적 라이브러리를 제공하고 있습니다.

FFMPEG 상용 컴포넌트도 Trial 버전은 시뮬레이터용 .so 파일만 제공하고,
실행 가능한 정품은 .a 파일을 링크해서 배포 하도록 하고 있는것 같습니다.

.a 파일이 존재하는 경로는 Search Path에 추가 하시면 되고,
LD 옵션에 추가되는 라이브러리를 추가 하시면 됩니다.

IOS의 경우 libbz2.dylib, libm.dylib가 추가로 필요하는데, 기존 라이브러리 목록에 빠져 있어서,
SDK Option에 추가 해야 됩니다.

최근에 맥과 IOS도 업데이트 되면서, 기존에 컴파일 했던 방식도 변경된 부분이 많아서,
기존에 llvm-gcc로 컴파일 했던것을 clang으로 해야 되더군요.

iTune의 공유파일를 통해서 동영상 파일을 추가 하려고 했는데, UIFileSharingEnabled 설정도
편법뿐 아직 Delphi에서 설정하는 방법은 없는것 같습니다.

ffmpeg를 컴파일해서 이미지를 보여주는 부분까지는 했는데, 아직 FireMonkey에서 TImage를 이용해서 보여주고 있어,
이미지 출력 주소를 이용해서 바로 보여주는 부분은 아직 찾지 못해 동영상이 출력시 계속 깜박거리고 있습니다.

아직 FireMonkey 관련 자료가 많이 부족해 너무 많이 고생했네요.
그 덕분에 많이 배우기도 했지만, 아직 컴파일러 자체적으로 개선해야 될 부분도 많아 보입니다.

컴파일 동영상

컴파일해서 안드로이드(옵티머스G)에 실행하고, Targer 대상을 IOS로 변경하여 컴파일후 IPAD에 실행하는 동영상입니다.
맥에서 VM으로 컴파일해서 컴파일 과정이 느립니다. 뛰어 넘어 가면서 보세요.

-

Android 라이브러리 패스 추가

libffmpeg.a, libhoffplay.a 파일이 존재하는 lib\android 경로 추가

Android에 컴파일시 필요한 라이브러리 추가

-lffmpeg -lcompiler_rt -lz 추가

IOS 라이브러리 추가

libbz2.dylib, libm.dylib 추가후 Update Local File Chahe 버튼를 누르세요.

IOS 라이브러리 패스 추가

libavcodec.a, libavdevice.a, libavformat.a, libavutil.a, libhoplayer.a, libswscale.a  파일이 존재하는 lib\ios 경로 추가

IOS에 컴파일시 필요한 라이브러리 추가

-lavcodec -lavdevice -lavformat -lavutil -lswscale -lz -lm -lbz2 추가

iTunes에서 파일공유 설정

동영상에서 보았듯이 컴파일시에 Deployment 파일 목록의 {프로젝트명}.info.plist 파일을 수정해야 적용됩니다.
(컴파일시마다 파일이 변경됨)

iTunes에서 파일공유에서 파일 확인


-

728x90
728x90

출처 : Mobile Tutorial: Using InterBase ToGo (iOS and Android)
http://www.pclviewer.com/android/
Linux Certif - dlopen
Getting the Battery Level on Android With Delphi
XE4 Mobile Tip #2 ? Loading local HTML content
Android/SQLite 공유 라이브러리를 이용하는 C로 프로그램 작성
Delphi XE4 with iOS / #08. Hello World without Firemonkey
Thread: What is the right procedure to add the additional framework on iOS ?
Unable to dlopen(libsomething.so) Cannot load library: link_image[1995]: failed to link libsomething.so

정적 라이브러리 참조

Androidapi.Log.pas (델파이 일부 소스 발취)

unit Androidapi.Log;

interface

{$I Androidapi.inc}

type
  android_LogPriority = (
    ANDROID_LOG_UNKNOWN,
    ANDROID_LOG_DEFAULT,
    ANDROID_LOG_VERBOSE,
    ANDROID_LOG_DEBUG,
    ANDROID_LOG_INFO,
    ANDROID_LOG_WARN,
    ANDROID_LOG_ERROR,
    ANDROID_LOG_FATAL,
    ANDROID_LOG_SILENT
 );
 {$EXTERNALSYM android_LogPriority}


function __android_log_write(Priority: android_LogPriority; const Tag, Text: MarshaledAString): Integer; cdecl;
  external AndroidLogLib name '__android_log_write';
 {$EXTERNALSYM android_LogPriority}

{ Helper functions }
function LOGI(Text: MarshaledAString): Integer;
...

implementation

function LOGI(Text: MarshaledAString): Integer;
begin
  Result := __android_log_write(android_LogPriority.ANDROID_LOG_INFO, 'info', Text);
end;

...

end.

정적라이브러리 소스

testlib.h

/*
 * testlib.h
 *
 *  Created on: 2012. 9. 6.
 *      Author: bluexmas
 */
 
#ifndef LIBTEST_H_
#define LIBTEST_H_
 
extern const char* get_version();
 
#endif /* LIBTEST_H_ */

testlib.c

/*
 * testlib.cpp
 *
 *  Created on: 2012. 9. 6.
 *      Author: bluexmas
 */
 
#include "testlib.h"
 
const char* get_version() {
  return "testlib ver 0.1";
}

Makefile

ANDROID_NDK_ROOT = /opt/android-ndk-r9
NDK_TOOLCHAIN_VERSION = 4.6
TOOLCHAIN = /opt/android-9-toolchain

CROSS  = $(TOOLCHAIN)/bin/arm-linux-androideabi-
INCDIR = $(TOOLCHAIN)/sysroot/usr/include
LIBDIR = $(TOOLCHAIN)/sysroot/usr/lib

CC     = $(CROSS)gcc
AR     = $(CROSS)ar $(ARFLAGS) r
RANLIB = $(CROSS)ranlib
NM     = $(CROSS)nm

CFLAGS = -g $(INC) -fPIC 

TARGET = libtest.so

OBJS = testlib.o
#SRCS = testlib.c

.SUFFIXES : .c .o 

all : $(TARGET)

$(TARGET) : $(OBJS)
	$(CC) -shared -Wl,-soname,$@ -o $@ $(OBJS)

dep :
	gccmakedep $(INCDIR) $(SRCS)

clean :
	rm -rf $(OBJS) $(TARGET) core

 컴파일

파일

testlib.h /testlib.c /Makefile

위에서 만들어진 정적 라이브러리 파일을 /sdcard 폴더에 복사한 뒤(직접복사), 동적으로 로딩하여 get_version 함수룰 호출하는 예제입니다. library\lib\armeabi\ 경로에서 상대경로로 읽으려고 했지만, 절대경로만 인식하는 것 같습니다. 참고 자료가 별로 없어 확인 할 수 없었습니다.

Unit2.pas

unit Unit2;

interface

uses
  Classes, SysUtils, SyncObjs,
  System.IOUtils, Posix.Dlfcn;

//function __get_version(): MarshaledAString; cdecl; external '/sdcard/libtest.so' name 'get_version';

type
  _get_version = function(): MarshaledAString; {$IFNDEF CLR}cdecl;{$ENDIF}

function LoadLib() : IntPtr;

var
  get_version : _get_version;

implementation

function LoadLib() : IntPtr;
begin
  //
  //hLiteLib2 := dlopen(MarshaledAString(GetHomePath() + PathDelim + 'libtest.so'), RTLD_LAZY);
  //hLiteLib2 := dlopen(MarshaledAString(TPath.Combine(TPath.GetDocumentsPath, 'libtest.so')), RTLD_LAZY);
  Result := dlopen(MarshaledAString('/sdcard/libtest.so'), RTLD_LAZY);

  if NativeUInt(Result) > 0 then begin
    get_version := dlsym(Result, MarshaledAString('get_version'));
  end;
end;

Initialization
  get_version := nil;

end.

Unit1.pas

unit Unit1;

interface

uses
  System.SysUtils, System.Types, System.UITypes, System.Classes, System.Variants, FMX.Types,
  FMX.Controls, FMX.Forms, FMX.Dialogs, FMX.WebBrowser, FMX.StdCtrls, FMX.Layouts,
  FMX.Edit, FMX.Memo;

type
  TForm1 = class(TForm)
    Button1: TButton;
    Button2: TButton;
    Edit1: TEdit;
    Memo1: TMemo;
    procedure Button1Click(Sender: TObject);
    procedure Button2Click(Sender: TObject);
  private
    { Private declarations }
    hLiteLib: IntPtr;
  public
    { Public declarations }
  end;

var
  Form1: TForm1;

implementation

uses
  System.IOUtils, Unit2;

{$R *.fmx}

procedure TForm1.Button1Click(Sender: TObject);
begin
  //ShowMessage(TPath.Combine(TPath.GetDocumentsPath, 'libtest.so'));

  hLiteLib := LoadLib();
  if NativeUInt(hLiteLib) = 0 then begin
    ShowMessage('Cannot load client library: ' + 'libtest.so');
  end;

  if Assigned(get_version) then begin
    Edit1.Text := get_version(); // IntToStr( hLiteLib );
  end;
end;

procedure TForm1.Button2Click(Sender: TObject);
begin
  Memo1.Lines.LoadFromFile(TPath.Combine(TPath.GetDocumentsPath, 'memo.txt'));
end;

end.

Deployment 화면

실행결과

실행결과 Button1 선택

실행결과 Button2 선택

728x90
728x90

출처 : Installing the Android Development Tools
Android Mobile Application Development
DELPHI XE4에서 FireMonkey Mobile용 컴포넌트 추가하기.

1. ShowModal 사용하기 그리고 문제점
ShowModal Dialogs in FireMonkey Mobile Apps

XE5에서 안드로이드의 경우 기존과 동일한 ShowModal를 지원하지 않는 것 같습니다.
안드로이드의 경우 모든 화면 제어가 쓰레드로 동작하기 때문에 기존의 ShowModal를 지원 못하는게 아닌가 싶습니다.

기존 코드

function GetConnectionInfo(AOwner: TComponent): TDBConnection;
var
  FConnect: TFConnect;
begin
  Result := nil;
  FConnect := TFConnect.Create(AOwner);
  try
    if FConnect.ShowModal = mrOk then
    begin
      Result := FConnect.ResultDBConnection;
    end;

  finally
    FConnect.Free;
  end;
end;

수정된 코드

var
  FConnect: TFConnect;           // 전역변수

function GetConnectionInfo(AOwner: TComponent; OnDBConnected : TNotifyEvent): TDBConnection;
var
  ConnectFormBase: TConnectFormBase;
begin
  Result := nil;
  try
    if Assigned(FConnect) then begin
      FConnect.Show;
    end else begin
      FConnect := TFConnect.Create(AOwner);
      FConnect.OnDBConnected := OnDBConnected;
      FConnect.ShowModal(procedure(ModalResult : TModalResult)
      begin
        if ModalResult = mrOk then begin
          FConnect.DoConnect();
          if Assigned(Self.OnDBConnected) then begin
            FConnect.OnDBConnected(FConnect.ResultDBConnection);
          end;
        end;
        if Assigned(FConnect) then
          FConnect.DisposeOf;
        FConnect := nil;

        frmMDIMain.Invalidate;
      end);
    end;
  except
    on e : Exception do
      ShowMessage(e.Message);
  end;
end;

지금 까지 찾아낸 문제점

1. ShowModal의 결과 값을 이벤트 형태로 받아 합니다.

FConnect.ShowModal(procedure(ModalResult : TModalResult)
begin
  f ModalResult = mrOk then begin
    // mrOk인 경우 처리
  end;

  // 화면 닫기
  frmMDIMain.DisposeOf;
end);

문제점1 : 변수 사용의 불편함 / In-Line으로 procedure를 구현하므로 전역으로 정의된 변수를 제외하고는 접근이 되지 않음

문제점2 : FormClose 호출 시점 

기존에는 if FConnect.ShowModal = mrOk then 이후 문장이 실행 되기 전에 FormClose가 호출 되었지만,
XE5 안드로이드의 경우 DisposeOf가 호출 시점에 FormClose가 호출 됩니다.

CallBack 이벤트 함수(OnDBConnected 함수)를 만들어서 처리했습니다.

2. 기존에 ShowModal 처럼 정지(?) 되지 않습니다.

문제점1ShowModal 이후 문장이 그대로 실행 됩니다.

ShowModal 이후 문장인 finally 가 바로 실행(FConnect.Free; 실행) 되어 화면이 나타났다가 빠르게 사라집니다.

문제점2 : 정지 되지 않기 때문에 ShowModal 호출하는 이벤트를 다시 호출하면 두개의 화면이 생성됩니다.

Form 변수인 FConnect 를 전역 변수로 선언하고, Assigned 유효 한지 체크하고, 종료시 FConnect := nil; 로 초기화 했습니다.

문제점3 : FireMonkey가 느려서 닫기 버튼을 두번 누르면, DisposeOf 가 두번 호출 되면서 FConnect가 닫혀지고, Main 화면까지도 닫아 지는 것 같은 문제가 있습니다. (버그로 의심됨)

문제점4 DisposeOf 가 호출되고 Main 화면이 나타나야 할 텐데, 화면이 그대로 남아 있어, Main 폼의 frmMDIMain.Invalidate; 를 호출해야 보여집니다. (버그로 의심됨)

- end -

728x90

+ Recent posts