블루투스 바코드 스캐너(BI-07) 안드로이드 라이브러리를 델파이에서 연동하는 방법

2017.03.20 17:03

S모전자에서 의뢰한 휴대용 바코드 스캐너와 연동한 내용 공유합니다.

휴대용 바코드 스캐너는 JAR 형태의 안드로이드 라이브러리를 제공합니다.

이 과정을 통해 델파이와 안드로이드 라이브러리 연동하는 내용을 소개합니다.


휴대용 바코드 스캐너 연동



위 영상의 휴대용 바코더 스캐너는 클래식 블루투스 기반으로 연결을 제공합니다.

해당 안드로이드 앱은 델파이로 제작했으며, JAR 파일을 이용해 스캐너와 연동합니다.


JAR 파일 연동과정은 아래와 같습니다.

1, 준비(라이브러리 준비 및 분석)

2, 델파이용 안드로이드 라이브러리 브릿지 파일 생성

3, 델파이 프로젝트에 안드로이드 라이브러리 추가

4, 브릿지 파일을 이용해 안드로이드 라이브러리 연동


기타 안드로이드 라이브러리 연동 시 참고사항

델파이 샘플 프로젝트


준비(라이브러리 준비 및 분석)

대상 장비(Point Mobile BI-07)

Point Mobile: BI-07

링크: http://www.pointmobile.co.kr/item.php?it_id=1381210470&ca_id=401010


라이브러리 준비 및 분석

안드로이드와 iOS 라이브러리를 제공합니다.

안드로이드 라이브러리(jar) :  PM3SDK_connectByAddress.jar

라이브러리 관련 문서 및 샘플 :  BI07PM3SDK_Document.chm

대부분의 라이브러리 제공 시 연동 문서와 샘플코드를 제공합니다.

문서를 잘 숙지하고, 샘플코드를 분석합니다. 대략적으로 소스코드를 파악하되 해당 라이브러리 호출하는 부분은 정확히 숙지해야 합니다.

델파이용 안드로이드 라이브러리 브릿지 파일 생성

델파이에서 안드로이드 라이브러리를 사용하기위해 브릿지 파일을 생성해야 합니다.

브릿지 파일 생성

Java2OP.exe를 이용 델파이 브릿지 파일을 생성합니다. Java2OP는 JAR 파일(*.jar) 또는 Java 소스파일을 호출할 수 있는 델파이 코드를 생성합니다.(참고: 파이어모키에서 외부 라이브러리 연동하기(jar, so, a))

아래 커맨드를 이용해 브릿지 파일(Androidapi.JNI.PM3SDK_BI07.pas)을 생성합니다.
D:\ProjectExt\SamsungElectronics>set path=%PATH%;"c:\Program Files (x86)\Embarcadero\Studio\18.0\bin\converters\java2op"

D:\ProjectExt\SamsungElectronics>java2op -jar .\SDK\PM3SDK_connectByAddress.jar -unit AndroidapiJNI.PM3SDK_BI07
Warning: error opening ReservedWordsOP.txt
Warning: error opening ReservedWordsC.txt
Parsing xml: c:\Program Files (x86)\Embarcadero\Studio\18.0\bin\converters\java2op\bootclasses.xml
Parsing jar: D:\ProjectExt\SamsungElectronics\SDK\PM3SDK_connectByAddress.jar

D:\ProjectExt\SamsungElectronics>

생성된 결과


델파이 프로젝트에 안드로이드 라이브러리 추가

라이브러리 추가

멀티-디바이스 폼 애플리케이션(파이어몽키) 프로젝트에서 대상 플랫폼(Target Platform)을 안드로이드로 설정 후 Libraries 항목에 JAR 파일(안드로이드 라이브러리)을 추가합니다.


델파이 브릿지 파일 추가

해당 라이브러리를 사용하기 위해 델파이 브릿지 파일(Androidapi.JNI.PM3SDK_BI07.pas)을 프로젝트에 추가합니다.

브릿지 파일 확인을 위해 컴파일 합니다.

만약, 컴파일 시 E2029 오류 발생 시 다음을 참고해 조치합니다.

E2029 오류 조치방법

E2029 오류는 예약어를 용도와 다른 곳에서 사용할 경우 발생합니다.

오류가 발생하는 예약어 키워드 앞에 "&"을 추가해 해결합니다.

(예> type -> &type, in -> &in)

(참고: http://tech.devgear.co.kr/delphi_qna/420082)

브릿지 파일을 이용해 안드로이드 라이브러리 연동

라이브러리 연동 문서와 샘플코드를 참고해 기능을 구현했습니다.

그 중 가장 빈번히 사용되는 자바 객체 생성과 처음 구현해본 자바 핸들러 콜백함수 구현, 가장 어렵게 변환했던 자바 오브젝트로 캐스팅된 바이트 배열에서 데이터 가져오기 3가지 내용 소개합니다.

자바 객체 생성

브릿지 파일은 라이브러리에 구현된 클래스 인터페이스가 다음과 같이 구현되어 있습니다.

(BluetoothChatService 클래스)

위와 같이 자바 클래스는 2개의 인터페이스와 1개의 클래스로 정의됩니다.


JBluetoothChatServiceClass 인터페이스는 클래스 함수와 속성, 상수를 제공합니다.(생성하기 전 사용)

JBluetoothChatService 인터페이스는 인스턴스 함수와 속성, 상수를 제공합니다.(생성해서 사용)


델파이에서 자바 객체 생성은 아래와 같이 클래스.JavaClass.Init(파라메터) 형식으로 구현합니다.

var
  FChatService: JBluetoothChatService;
...
  FChatService := TJBluetoothChatService.JavaClass.init(Context, FHandler);

자바 핸들러 콜백함수 구현

바코드 스캐너에서 바코드 데이터 수신은 핸들러의 콜백함수를 통해 받습니다.

그 자바코드는 아래와 같습니다.
private final Handler mHandler = new Handler() {
}

mChatService = new BluetoothChatService(mContext, mHandler);
SendCommand.SendCommandInit(mChatService, mHandler);

델파이에서는 다음과 같이 구현했습니다.

1) 핸들러 콜백함수 정의

type
  THandleMessageEvent = procedure(msg: JMessage) of object;
  TCharServiceHandlerCallback = class(TJavaLocal, JHandler_Callback)
  private
    FOnHandleMessage: THandleMessageEvent;
  public
    function handleMessage(msg: JMessage): Boolean; cdecl;

    property OnHandleMessage: THandleMessageEvent read FOnHandleMessage write FOnHandleMessage;
  end;

주의할 점은, 인터페이스(JHandler_Callback)를 상속받은 메소드(handleMessage)는 public 영역에 구현해야 합니다.

습관적으로 델파이와 같이 private 영역에 선언 시 "Segmentation fault (11)" 오류(잘못된 메모리 영역 참조)로 상당히 고생했습니다.


2) 핸들러, 콜백 객체 생성, 이벤트 연결(OnHandleMessage)

  private
    FHandler: JHandler;
    FCallback: TCharServiceHandler;

  FCallback := TCharServiceHandlerCallback.Create;
  FCallback.OnHandleMessage := ChatServiceHandleMessage;

  Context := TAndroidHelper.Context;
  Looper := TJLooper.JavaClass.getMainLooper;
  FHandler := TJHandler.JavaClass.init(Looper, FCallback);

  FChatService := TJBluetoothChatService.JavaClass.init(Context, FHandler);
  TJSendCommand.JavaClass.SendCommandInit(FChatService, FHandler);

콜백 객체 생성 후 OnHandleMessage 이벤트를 연결해 데이터를 수신합니다.

핸들러 생성 시 콜백 객체를 파라메터로 전달합니다.

핸들러 객체는 BluetoothChatService와 SendCommand 객체 생성 시 파라메터로 전달합니다.


3) 핸들러 콜백과 연결된 이벤트 발생

procedure TPointMobileBluetoothChatService.ChatServiceHandleMessage(
  msg: JMessage);
var
  DeviceName, JData: JString;
begin
  Log.d(msg.what.ToString);
  case msg.what of
  MESSAGE_STATE_CHANGE:
    ProcessHandlerStateChage;
  MESSAGE_BARCODE:
    ProcessHandlerBarcord(msg.obj, msg.arg1);
  MESSAGE_DEVICE_NAME:
    begin
      DeviceName := msg.getData.getString(StringToJString('device_name'));
      Log.d('Device name: ' + JStringToString(DeviceName));
    end;

  // 필요한 이벤트 추가 할 것
  end;
end;

스캐너 라이브러리는 바코드 정보 수신 시 핸들러 콜백과 연결된 이벤트로 데이터를 제공합니다.(이벤트가 발생합니다.)

자바 오브젝트로 캐스팅된 바이트 배열(byte[]) 데이터 가져오기

어디에서도 참고할 내용이 없어 가장 고생했던 내용입니다. 델파이 내부 구조를 분석하고 예측해서 구현했습니다.


자바에서 다음과 같이 오브젝트를 바이트 배열로 치환(캐스팅)하는 코드입니다.

byte[] BarcodeBuff = (byte[]) msg.obj;


델파이에서는 다음과 같이 구현했습니다.

function JObjectToStr(const AJObject: JObject): string;
var
  LObj: ILocalObject;
  ObjID: Pointer;
  JBytes: TJavaArray;
  JData: JString;
begin
  Result := '';

  if JStringToString(AJObject.getClass.getCanonicalName) <> 'byte[]' then
    Exit;

  if AJObject.QueryInterface(ILocalObject, LObj) = 0  then
    ObjID := LObj.GetObjectID
  else
    Exit;

  JBytes := TJavaArray(WrapJNIArray(ObjID, TypeInfo(TJavaArray)));
  JData := TJString.JavaClass.init(JBytes, 0, JBytes.Length);
  Result := JStringToString(JData);
end;

var
  Data: string;
begin
  Data := JObjectToStr(msg.obj);
end;


안드로이드 라이브러리 연동 참고사항

가장 좋은 참고자료는 파이어몽키 소스코드였습니다.


델파이 설치 경로 하위 source 디렉토리에는 파이어몽키 라이브러리 소스코드들이 포함되어 있습니다.(정식 제품의 경우에 한함)


그중 아래 경로의 소스코드들을 참고하시기 바랍니다.(Delphi 10.1 베를린 버전 기준)


안드로이드 SDK 브릿지 파일들

안드로이드 SDK 델파이 브릿지 파일이 포함되어 있습니다. 자바 소스코드의 객체이름을 다음 경로의 소스코드들에서 찾아 필요한 경우 uses 절에 추가 후 사용합니다.

C:\Program Files (x86)\Embarcadero\Studio\18.0\source\rtl\android

파이어몽키 라이브러리

다음 경로 중 FMX.AddressBook.Android.pas과 같이 Android가 포함된 파일들을 참고하기 바랍니다.

위 안드로이드 SDK 브릿지 파일들을 이용한 코드들로 구현되어 있습니다.

C:\Program Files (x86)\Embarcadero\Studio\18.0\source\fmx



델파이 샘플 프로젝트


기타 궁금한 내용은 댓글 또는 http://blog.hjf.pe.kr/452의 댓글을 참고하거나 등록해주세요.

저작자 표시 비영리 동일 조건 변경 허락
신고
크리에이티브 커먼즈 라이선스
Creative Commons License

험프리.김현수 험프리.김현수 파이어몽키 FMX, JNI, PointMobile, 바코드스캐너, 안드로이드

  1. Blog Icon
    silkroad99

    안녕하세요
    많은 도움을 받고 있습니다.
    혹 PM3SDK_connectByAddress.jar 파일은 어디서 다운받는건가요

  2. 이런, 제가 링크를 안걸었네요^^
    다운로드 링크 추가했습니다.

  3. Blog Icon
    barcoder

    위에 소스를 다운받아 컴파일시에 오류가 발생합니다..
    시애틀 사용자구요..
    메시지는 아래와 같습니다..

    Checking project dependencies...
    Compiling PointMobileBI07.dproj (Debug, Android)
    dccaarm command line for "PointMobileBI07.dpr"
    f:\program files (x86)\embarcadero\studio\17.0\bin\dccaarm.exe -$O- --no-config -M -Q -TX.so -AGenerics.Collections=System.Generics.Collections;
    Generics.Defaults=System.Generics.Defaults;WinTypes=Winapi.Windows;WinProcs=Winapi.Windows;DbiTypes=BDE;DbiProcs=BDE;DbiErrs=BDE -DDEBUG
    -E.\Android\Debug -I"f:\program files (x86)\embarcadero\studio\17.0\lib\Android\debug";"f:\program files
    (x86)\embarcadero\studio\17.0\lib\Android\Release" -LEC:\Users\Public\Documents\Embarcadero\Studio\17.0\Bpl\Android
    -LNC:\Users\Public\Documents\Embarcadero\Studio\17.0\Dcp\Android -NU.\Android\Debug -NSSystem;Xml;Data;Datasnap;Web;Soap; -O"f:\program files
    (x86)\embarcadero\studio\17.0\lib\Android\Release" -R"f:\program files (x86)\embarcadero\studio\17.0\lib\Android\Release" -U"f:\program files
    (x86)\embarcadero\studio\17.0\lib\Android\debug";"f:\program files (x86)\embarcadero\studio\17.0\lib\Android\Release" --libpath:"F:\Program Files
    (x86)\Embarcadero\Studio\15.0\PlatformSDKs\android-ndk-r9c\platforms\android-14\arch-arm\usr\lib";"F:\Program Files
    (x86)\Embarcadero\Studio\15.0\PlatformSDKs\android-ndk-r9c\sources\cxx-stl\gnu-libstdc++\4.8\libs\armeabi-v7a" --linker:"F:\Program Files
    (x86)\Embarcadero\Studio\15.0\PlatformSDKs\android-ndk-r9c\toolchains\arm-linux-androideabi-4.6\prebuilt\windows\bin\arm-linux-androideabi-ld.exe" -V
    -VN -NO.\Android\Debug PointMobileBI07.dpr
    [DCC Error] PointMobileBluetoothChatService.pas(147): E2035 Not enough actual parameters
    [DCC Warning] PointMobileBluetoothChatService.pas(158): W1000 Symbol 'SharedActivityContext' is deprecated: 'Use TAndroidHelper.Context'
    [DCC Error] PointMobileBluetoothChatService.pas(160): E2250 There is no overloaded version of 'CallInUIThread' that can be called with these arguments
    [DCC Error] PointMobileBluetoothChatService.pas(308): E2250 There is no overloaded version of 'Queue' that can be called with these arguments
    [DCC Fatal Error] MainForm.pas(18): F2063 Could not compile used unit 'PointMobileBluetoothChatService.pas'
    Failed
    Elapsed time: 00:00:02.9
    [Warning] W1000 Symbol 'SharedActivityContext' is deprecated: 'Use TAndroidHelper.Context'
    [Warning] W1000 Symbol 'SharedActivityContext' is deprecated: 'Use TAndroidHelper.Context'

  4. 해당 프로젝트는 10.1 베를린에서 작성되었습니다.
    버전별로 함수의 파라메터가 다를 수 있으니, 시애틀로 컴파일 시 오류를 확인해 조치하시기 바랍니다.

  5. Blog Icon
    silkroad99

    안녕하세요
    샘플 프로젝트를 베를린에서 테스트 해보고 있습니다.
    프로젝트 소스에서 압축을 풀면

    begin
    Application.Initialize;
    AApplication.CreateForm(TForm1, Form1);
    pplication.Run;
    end.

    상단에서 압축문제인지 잘 못된것을 수정하고나서
    컴파일하고 실행하면 바코드 스캐너가 스캔을 못합니다.

    메모장에 커서를 놓으면 바코드 리딩이 되는데
    스캔 버튼 클릭하면 작동이 안됩니다.

    메모장이나 에디트박스에서 커서 위치시키고
    스캔해야만 됩니다.
    제가 뭐를 잘못하고 있는건가요

  6. 위 영상을 보시면 아시겠지만, 저는 메모나 에디터에 커서를 놓지 않아도 스캔이 잘 동작합니다.

    자세한 내용은 github 소스코드와 설치한 소스코드를 비교해보시거나, 디버깅(F9 버튼으로 실행)해보시며 찾아보시기 바랍니다.

    질문만 보고는 제가 조언드릴 부분이 없네요.

  7. Blog Icon
    silkroad99

    빠른 답변에 감사드립니다.

    여기에는 파일 업로드를 할수없어
    부득이하게 데브기어 개발자QnA에
    프로젝트를 업로드했습니다.

    가능하시다면 어디가 오류인지 부탁드립니다.

    감사합니다.

  8. http://tech.devgear.co.kr/delphi_qna/432793
    위 글에 답변 달았습니다.

  9. Blog Icon
    silkroad99

    감사합니다.

  10. 안녕하세요.

    델파이 10.1 베를린 사용자 입니다.

    안드로이드가 아닌 윈도우 프로그램으로 자바파일을 호출하여 델파이에서 음성입력과 출력이 가능한 프로젝트를 구성해 보려고 하는데 위의 사항과 동일한 방법을 사용해도 무관할까요?

    음성인식과 음성출력은 자바에서 사용하였습니다.

    혹여 가능하시다면 답변부탁드립니다.

  11. 저도 윈도우 환경에서 자바파일 호출하는 작업을 진행해보지는 않았지만,
    개념은 비슷할 것으로 보입니다.

    다만, 안드로이드 환경에서는 필요한 기능이 유닛과 함수로 준비되어 있지만, 윈도우 환경에서는 필요한 기능들을 안드로이드 코드를 참조하는 등으로 새로 구성해야 할것으로 예상됩니다.

    구글에서 "call java from delphi" 등의 검색어로 검색해 보시면 관련된 자료들을 찾을 수 있으니 참고하시기 바랍니다.

  12. Blog Icon
    파이어버드

    위 소스 실행시키면
    Context := TAndroidHelper.Context;

    < 오류내용 >
    [DCC Error] PointMobileBluetoothChatService.pas(264): E2003 Undeclared identifier: 'TAndroidHelper'
    [DCC Error] PointMobileBluetoothChatService.pas(264): E2066 Missing operator or semicolon
    [DCC Error] PointMobileBluetoothChatService.pas(364): E2250 There is no overloaded version of 'Queue' that can be called with these arguments


    procedure TPointMobileBluetoothChatService.SetupChatService;
    var
    Context: JContext;
    Looper: JLooper;
    begin

    if FBluetoothAdapter.State = TBluetoothAdapterState.Off then
    begin
    Log.d('Turn on bluetooth');
    Exit;
    end;

    FCallback := TCharServiceHandlerCallback.Create;

    FCallback.OnHandleMessage := ChatServiceHandleMessage;

    //이부분이 에러가 납니다.
    Context := TAndroidHelper.Context;

    Looper := TJLooper.JavaClass.getMainLooper;

    FHandler := TJHandler.JavaClass.init(Looper, FCallback);

    FChatService := TJBluetoothChatService.JavaClass.init(Context, FHandler);
    TJSendCommand.JavaClass.SendCommandInit(FChatService, FHandler);

    end;


    xe8로 개발하고 있습니다.

    혹시 아시는 분 있으시면 답변 부탁드립니다.

    감사합니다.

  13. XE8에서는 TAndroidHelper.Context 대신 SharedActivityContext라고 선언하면 됩니다.

    참고로, 모바일 개발 시 되도록 최신버전으로 진행하는 것이 좋습니다.
    최신버전에서 새로운 안드로이드/iOS 플랫폼을 정식지원합니다.

  14. Blog Icon
    파이어버드

    안녕하세요. 험프리님
    xe8에서
    TAndroidHelper.Context -> SharedActivityContext 변경하니 잘 됩니다.
    이것 때문에 몇일동안 고생했는데 너무 감사합니다.

    그리고 silkroad99님도 원격 및 소스도 주시면서 도와 주셔서 감사하고
    두분 언제 저녁 한번 대접하고 싶은데 가능하신지...
    메일 주시면 달려가겠습니다.

    감사합니다.







  15. Blog Icon
    장창원

    안녕하세요. 자료 잘 봤습니다.
    delphi 10. berlin 쓰고 있습니다.
    머 좀 여쭤봐도 될른지요...

    제가 가지고 있던 java 파일을 jar을 만들었고, java2op 를 쓰는데, 여기서 문제가 생겼습니다.
    java2op -jar Recognition.jar -unit Android.JNI.Recognition -classes *
    라고 해서, 맨 뒤에 -classes * 을 붙이지 않으면, no classes selecte 라고 에러가 나더이다.
    여튼 붙여서 작업을 하면 돌아는 갑니다만, 결과로 나온 pas파일이 다 똑같습니다.
    java2op -jar FreeImage.jar -unit Android.JNI.FreeImage -classes * 라고 해서,
    FreeImage.jar 란 파일을 돌려도, 물론 java 파일의 내용은 틀리고, 안에 몇줄 없습니다만, 결과로 나온 pas 가 둘다 똑같습니다. 아무래도 bootclasses.xml 파일을 parsing 한다고 메세지가 나오던데, xml 과 비슷한 내용만 들어가 있습니다. 파일도 엄청 크고요...
    뭐가 문제인지 삽질 중입니다.
    고견 있으시면 댓글 부탁드립니다.
    감사합니다.

앱 완전 구동 후 작업 수행 하기(라이프 사이클 이벤트 / 타이머 이용)

2014.07.11 16:04

앱에서 초기화 작업을 FormCreate(또는 FormShow)에서 하게되면 구동시간이 길어지고, 오류가 발생하는 경우 앱이 정상구동되지 않을 수(검은화면만 표시) 있습니다.


그렇기 때문에 앱이 완전 구동된 이후에 초기화 작업을 진행하도록 하기 바랍니다.

앱 완전 구동 후 동작하기 위해서는 아래와 같은 2가지 방법이 있습니다.

  1. 모바일 앱 라이프 사이클 이용하기
  2. 타이머를 이용해 구동 이후 이벤트 받아 처리하기

모바일 앱 라이프 사이클 이용하기

모바일(안드로이드, iOS)는 라이프 사이클 이벤트를 제공합니다.(http://blog.hjf.pe.kr/114 참고)

아래 코드를 참고하기 바랍니다.

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.Platform;

type
  TForm1 = class(TForm)
    procedure FormCreate(Sender: TObject);
  private
    { Private declarations }
    FInit: Boolean;
    procedure InitData;

    function HandleAppEvent(AAppEvent: TApplicationEvent;
      AContext: TObject): Boolean;
  public
    { Public declarations }
  end;

var
  Form1: TForm1;

implementation

{$R *.fmx}

procedure TForm1.FormCreate(Sender: TObject);
var
  EventService: IFMXApplicationEventService;
begin
  FInit := False;
  if TPlatformServices.Current.SupportsPlatformService(IFMXApplicationEventService, IInterface(EventService)) then
    EventService.SetApplicationEventHandler(HandleAppEvent)
  else
    InitData;
end;

function TForm1.HandleAppEvent(AAppEvent: TApplicationEvent;
  AContext: TObject): Boolean;
begin
  case AAppEvent of
    TApplicationEvent.FinishedLaunching, 
    TApplicationEvent.BecameActive:
        InitData;
  end;
  Result := True;
end;

procedure TForm1.InitData;
begin
  if FInit then
    Exit;

  // 데이터 및 컨트롤 초기화

  FInit := True;
end;

end.


참고로 FinishedLaunching 이벤트가 iOS에서 발생하지 않습니다. 그래서 iOS의 경우 BecameActive 이벤트를 이용해 초기화 진행했습니다.

BecameActive 이벤트는 앱 활성화 될 때 마다 발생하기 때문에 중복방지 코드(if FIni then Exit;)를 추가했습니다.

타이머를 이용해 구동 이후 이벤트 받기

타이머(TTimer) 컴포넌트를 폼에 추가 후 실행하면, 앱이 구동된 이후 타이머 이벤트가 발생합니다.

procedure TForm1.Timer1Timer(Sender: TObject);
begin
  Timer1.Enabled := False;
  InitData;
end;

관련글



저작자 표시 비영리 동일 조건 변경 허락
신고
크리에이티브 커먼즈 라이선스
Creative Commons License

험프리.김현수 험프리.김현수 파이어몽키 FMX, Log.d, 델파이, 라이프사이클

Instagram Filter 효과와 같은 앱(소스 포함)

2014.04.09 11:21


인스타그램 앱과 같이 사진에 다양한 필터를 적용하는 앱에 대한 소스코드가 공개되 었습니다.

아시다 시피 파이어몽키에는 60여종의 다양한 이펙트(TEffect)가 제공(62개의 파이어몽키 이미지 효과(Effects))됩니다. 이펙트를 이용해 아주 쉽게 위와 같은 동작을 하는 앱을 만들 수 있습니다.


간단한 구현 원리를 추가하면, 

  • 첫번째 탭에서 원본이미지에 TSelection 컴포넌트를 올리고 영역을 지정합니다.
  • 두번째 탭에서 상단의 이미지에 앞에서 선택한 영역의 이미지를 표시하고, 아래에는 다양한 효과(TEffect)가 적용된 이미지를 놓고, 이미지 선택 시 TFilterEffect.ProcessEffect 메소드를 적용합니다.
  • 세번째 탭에서는 이펙트가 적용된 이미지를 ActionList의 ShowShareSheetAction을 통해 공유 및 저장합니다.


더 자세한 내용은 아래의 소스코드를 통해 확인하세요.



참고링크



저작자 표시 비영리 동일 조건 변경 허락
신고
크리에이티브 커먼즈 라이선스
Creative Commons License

험프리.김현수 험프리.김현수 파이어몽키 FMX, TEffect

Sheet Animation 컴포넌트(움직이는 캐릭터 컴포넌트) 소개

2014.02.27 13:04

안녕하세요. 험프리.김현수입니다. 


오늘은 필요할듯 말듯한 캐릭터 애니메이션 컴포넌트 하나  소개합니다. 


TSprite라는 컴포넌트인데요. 오른쪽처럼 사용됩니다.

동작원리는 한장의 이미지에서 영역을 이동하며 화면에 출력하는 방식입니다.


일반적으로 캐릭터 애니메이션이 게임같은 곳에서 많이 사용되는데요. 제가 생각하기론 앱의 기능을 소개하는 부분이나 움직이는 이모티콘 등에 활용한다면 앱의 완성도를 높이고, 풍부한 기능구현이 가능 할 것 같습니다.


오른쪽의 데모는 소스코드에 포함된 데모이구요. 뒤집기, 회전, 반전, 반복등의 기능을 자체 제공하고 있습니다.


아래와 같은 이미지가 순차적으로 표시됩니다.



컴포넌트를 다운로드 받으면 소스가 포함되어 있어 어떤식으로 구현되어 있는지 확인하실 수 있어 도움이 되실 것 같습니다.


설치안내

컴포넌트 설치 후 사용 시 몇가지 이슈가 있어 설치 및 대처방안을 남깁니다.


설치방법

  1. 아래에서 컴포넌트 다운로드 후 좋은(컴포넌트를 모아두는) 디렉토리로 이동합니다.(델파이에 경로 지정이 필요)
  2. source 디렉토리에서 FireBlaze.dpk를 열고 설치합니다.(프로젝트 메니저에서 Install 하면 됩니다.)
  3. 델파이에서 Tool > Options > Environment Options > Delphi Options > Library > Library path에 위의 source 경로를 추가합니다.
설치시 오류대응


만약 위와 같은 오류가 실행 시 발생하는 경우 아래의 단계를 진행하세요.

  1. FBSprite.pas 파일 오픈
  2. TSprite의 published 영역에 아래의 코드 추가
   property Position;
   property Width;
   property Height;


다운로드


참고링크



저작자 표시 비영리 동일 조건 변경 허락
신고
크리에이티브 커먼즈 라이선스
Creative Commons License

험프리.김현수 험프리.김현수 파이어몽키 Animation, Firemonkey, FMX, 컴포넌트, 파이어몽키

RESTful 웹서비스 연동(Rest Client 소개)

2014.02.24 23:21

현재 대상 서비스(discogs.com)에 인증 인터페이스가 추가되어 예제를 따라하면 인증오류가 발생합니다. 하지만 이 글을 통해 REST Client의 개념을 잡는데 도움이 될 것입니다.



안녕하세요. 험프리.김현수입니다.


모바일 개발 세미나 및 자료들에서 원격 데이터 연결 시 DataSnap을 강조하다 보니 원격지 데이터 연결 시 DataSnap만 써야 되는 것으로 오해가 종종있어, 기존 구성된 사이트에 RESTful(Http 요청)방식으로 연결하는 방법을 소개합니다.


여지껏 원격지 데이터를 받아오기 위해 일반적으로는 IdHttp 컴포넌트를 이용해 Get(또는 Post)하여 Response 데이터를 받아온 후 Parser를 이용해 데이터를 파싱하고, 파싱된 데이터를 사용하는 방법등 코딩 위의 방법이 대부분이었습니다.


이번에 소개해 드릴 컴포넌트는 XE5에서 새로 추가된 REST 컴포넌트입니다.


REST 컴포넌트는 RestClient, RestRequest, RestResponse 3가지 주요 컴포넌트를 주축으로 구성되어 있으며, 요청과 응답을 객체화 시키고, 응답의 경우 RESTResponseDataSetAdapter를 연결하여 사용자의 데이터 파싱없이 리스트 및 그리드에서 사용할 수 있는 DataSource로 데이터를 자동변환합니다. 


Source(대상) REST API 분석 및 준비

▶ 대상 사이트 API 분석

음반정보 제공 사이트인 discogs에 오픈된 API를 이용하겠습니다. 그중 Search API를 사용합니다.


대상정보

  • Request : http://api.discogs.com/database/search?q=psy&type=master&per_page=20
  • Response : JSON 포맷으로 응답, "results" 항목 이후의 배열데이터 사용(Search API 참고


REST Debugger을 실행하고 아래와 같은 정보를 입력합니다.(REST Debugger 설치 및 사용방법)

  • URL : http://api.discogs.com
  • Resource : database/search?q={query}&type=master&per_page={page}
    • query : psy
    • page : 20

[Send Request] 버튼을 누르고 응답이 성공한 경우 하단의 [Body] 탭에서 Root Element에 "results"를 입력 후 [Apply] 버튼을 누른 후 [Tabular Data] 탭으로 이동 후 목록이 표시되었는지 확인합니다.


(※ REST Debugger도 REST 컴포넌트 기반으로 개발되어 있습니다.)

REST 컴포넌트 추가

▶ 모바일 프로젝트 생성 및 데이터모듈 추가

새로운 프로젝트 생성 후 Data Module을 추가합니다.(Project Manager > Project 선택 > Add New > Other > Data Module)

아래와 같이 컴포넌트를 추가하고 속성값을 변경합니다.


RESTClient

  • BaseURL : http://api.discogs.com

RESTRequest

  • Client : RESTClient1
  • Resource : database/search?q={query}&type=master&per_page={page}
  • Params : query=psy, page=20

RESTResponse

RESTResponseDataSetAdapter

  • DataSet : ClientDataSet1
  • Response : RESTResponse1
  • RootElement : results

ClientDataSet

위와 같이 구성 후 RESTRequest1을 더블클릭(또는 우측마우스 > Execute) 하면 성공 시 "Response: 200 - HTTP/1.1 200 OK" 메시지가 실패 시 오류메시지가 표시됩니다.

연결이 성공한 경우 RESTResponse1의 Content항목을 선택하시면 응답받은 데이터를 확인할 수 있습니다.(만약, 목록이외의 항목이 필요한 경우 RESTResponse 컴포넌트의  Content 항목을 활용할 수도 있습니다.)

UI 구성(ListView 및 검색항목 추가)

위의 Data Module을 폼의 Uses 절에 추가합니다.(File > Use unit 후 선택)

(유닛명 및 컨트롤 명은 임의로 명명하였으니 본인의 이름으로 사용하세요.)

▶ 리스트 추가(ListView), 데이터연결

빈 폼에 ListView 하나 추가 후 align 속성을 "alClient"로 변경해 목록이 가득차도록 합니다. 

아래와 같이 DataModule의 ClientDataSet과 ListView의 LiveBinding을 연결해 리스트에 데이터를 표시합니다.


Live Binding 윈도우 표시하기 - Object Inspector 아래 Bind Visually를 누르면 메인폼 아래에 표시됩니다.


사실 위와 같이만 구성해도 얼마나 쉽게 원격지의 데이터를 사용할 수 있는지 느낌이 오실거에요.

▶ 검색 항목 추가

그럼 추가로 검색 UI를 추가하고 검색 인터페이스를 연동해보겠습니다.

검색 메소드 추가

DataModule 유닛의 public 절에 아래와 같이 검색용 Search메소드를 추가합니다.

type
  TDataModule9 = class(TDataModule)
    RESTClient1: TRESTClient;
    RESTRequest1: TRESTRequest;
    RESTResponse1: TRESTResponse;
    RESTResponseDataSetAdapter1: TRESTResponseDataSetAdapter;
    ClientDataSet1: TClientDataSet;
  private
    { Private declarations }
  public
    { Public declarations }
    procedure Search(AKeyword: string);
  end;

var
  DataModule9: TDataModule9;

implementation

{%CLASSGROUP 'FMX.Controls.TControl'}

{$R *.dfm}

{ TDataModule9 }

procedure TDataModule9.Search(AKeyword: string);
begin
    RESTRequest1.Params.ParameterByName('query').Value := AKeyword;
    RESTRequest1.Execute;
end;

위의 RESTRequest1.Params를 변경하고 실행(RESTRequest1.Execute;)하는 동작으로 요청 파라메터를 변경할 수 있습니다.

UI 추가

검색 키워드 입력을 위해  TEdit를 Form에 추가합니다.(가끔 ListView에 추가하는 경우가 있으니 주의하세요)

align 속성을 "alTop"으로 변경합니다.


Edit의 OnChange에 아래와 같이 코딩합니다.

procedure TForm8.Edit1Change(Sender: TObject);
begin
  DataModule9.Search(Edit1.Text);
end;

배포 및 확인

REST 컴포넌트를 이용한 경우 내부적으로 MIDAS를 이용하기 때문에 MIDAS Library를 함께 배포해야 합니다.

배포 메뉴에서 기능파일 추가(Project > Deployment > Add featured files) 메뉴를 이용해 MIDAS Library를 추가합니다.



기기배포 등의 확인 단계는 설명을 생략하니 각자의 디바이스에서 확인해 보시기 바랍니다.


목록표시 및 요청 등의 인터페이스를 설명하였습니다. 등록요청 등의 인터페이스는 위의 내용을 조금더 확장하시면 충분히 구현가능 하실 것 같습니다.


다음에는 XML을 이용한 외부 서비스 연동에 대해서도 살펴보도록 하겠습니다.


감사합니다.


관련정보


추가정보



    저작자 표시 비영리 동일 조건 변경 허락
    신고
    크리에이티브 커먼즈 라이선스
    Creative Commons License

    험프리.김현수 험프리.김현수 파이어몽키 Firemonkey, FMX, JOSN, REST Debugger, RESTCilient, RESTful, 파이어몽키

    Firemonkey에서 Android Toast Message 사용하기

    2013.12.15 23:54

    Android API를 Firemonkey에서 사용하는 방법을 설명하기 위해 

    Toast Message를 사용하는 방법을 샘플로 만들어 봤습니다.

    (다음 글에서는 컨버팅하는 방법을 설명하겠습니다.)


    아래의 화면과 같이 위치를 지정해서 토스트메시지를 출력하면 Lock걸지 않는 메시지가 나왔다가 사라집니다.

    (아래의 샘플은 100% Firemonkey(delphi)로 만들어 졌습니다.)



    혹시 확인해 보고 싶으신 분은 아래 APK 다운로드 해보세요.^^(당연히 iOS에서는 동작하지 않습니다.)



    토스트 메시지는 아래의 문서를 참고해서 컨버팅 했구요.

    http://developer.android.com/reference/android/widget/Toast.html


    토스트 메시지 위치를 지정하기 위해 아래의 문서도 컨버팅 했습니다.(그냥 상수처리해도 될걸 그랬는데요. 내주말 ㅠㅜ)

    http://developer.android.com/reference/android/view/Gravity.html


    관련된 소스는 아래의 Github사이트를 참고해주세요.(Follow 해주시면 감사하구요^^)

    https://github.com/hjfactory/FMX.Devgear/tree/master/Samples/Android_Toast


    대략 소스는 아래와 같습니다.



    다음 글에서는 안드로이 API를 파이어콩키에서 사용하는 자세한 방법을 설명해 보겠습니다.

    감사합니다.

    저작자 표시 비영리 동일 조건 변경 허락
    신고
    크리에이티브 커먼즈 라이선스
    Creative Commons License

    험프리.김현수 험프리.김현수 파이어몽키 android, Converte android to firemonkey, Firemonkey, FMX, toast, Toast Message

    [FMX] 다이나믹한 UI 효과 - Float animation effect

    2013.03.05 16:35

    대부분의 분들은 파이어몽키로 멀티플랫폼 지원을 위한 개발을 생각하고 계실텐데요.

    파이어몽키는 멀티플랫폼 기능외에도 다양한 기능이 포함되어 있습니다.


    대표적으로 

    3D 효과 및 벡터형식의 부드러운 출력, RoateAngle 및 Scale 등의 신규 속성 등이 추가되었습니다.


    오늘은 

    새로운 기능 중 하나인 FloatAnimation에 대해 소개하려 합니다.


    FloatAnimation은 단어 그대로 Float을 Animation하는 놈입니다.


    좀 더 자세히 설명하면, 

    지정한 Start와 Stop에 해당하는 Float 값사이를 지정된 시간(Duration)에 맞춰 다양한 효과(Interpolation: 보간)로 값을 변경하는 놈입니다.


    우선 샘플을 보시면 아래의 영상은 마우스 휠(업/다운) 시 마우스 주변에 효과를 주고. 특정 Zoom 이상인 경우 RoundRect 메시지를 표시하고 서서히 사라지는 효과를 주었습니다.

    (구현 내용은 아래 첨부한 소스파일을 참고하시구요.)


    FloatAnimation은 TAniThread(FMX.Types.pas)에 의해 쓰래드로 진행이 되어 

    다른작업에 영향을 주지 않고도 UI에 다양한 효과 및 재미난 기능을 매우 쉽게 넣을 수 있습니다.


    FloatAnimation을 사용하는 방법은 두가지 인데요.


    첫째, 

    애니메이션 효과를 줄 컨트롤의 자식으로 TFloatAnimation을 등록 후 설정하여 사용하는 방법

    둘째,

    애니메이션 효과를 줄 컨트롤에 .AnimationFloat() 메소드를 이용하여 직접 효과를 주는 방법


    두개 중 편리한 방법을 사용하시면 될 것 같습니다.


    아래의 영상은

    AnimationType과 TInterpolationType을 지정하고 어떤 효과가 나는지 확인하는 샘플입니다.

    (당겼다가 가기[Back], 공튀기기[Bounce] 등의 효과를 확인 할 수 있습니다.)


    그리고 위 영상의 주요코드


    uses
      System.TypInfo;
    
    procedure TForm3.FormCreate(Sender: TObject);
    var
      Animation: TAnimationType;
      Interpolation: TInterpolationType;
    begin
      Circle1.Position.X := 0;
      Circle1.Position.Y := (Panel1.Height - Circle1.Height) / 2;
    
      // Circle이 멈출 위치를 계산
      FLastValue := Panel1.Width - Circle1.Width;
    
      // 콤보박스에 속성을 표시합니다.
      for Animation in [Low(TAnimationType)..High(TAnimationType)] do
        cbbAnimation.Items.AddObject(GetEnumName(TypeInfo(TAnimationType), Ord(Animation)), TObject(Animation));
      cbbAnimation.ItemIndex := 0;
    
      for Interpolation in [Low(TInterpolationType)..High(TInterpolationType)] do
        cbbInterpolation.Items.AddObject(GetEnumName(TypeInfo(TInterpolationType), Ord(Interpolation)), TObject(Interpolation));
      cbbInterpolation.ItemIndex := 0;
    end;
    
    procedure TForm3.Button1Click(Sender: TObject);
    begin
      if rbTFloatAnimation.IsChecked then
      begin
        FloatAnimation1.PropertyName := 'Position.X';
        FloatAnimation1.StartValue := 0;
        FloatAnimation1.StopValue := FLastValue;
        FloatAnimation1.Duration := 1;
        FloatAnimation1.AnimationType := TAnimationType(cbbAnimation.Items.Objects[cbbAnimation.ItemIndex]);
        FloatAnimation1.Interpolation := TInterpolationType(cbbInterpolation.Items.Objects[cbbInterpolation.ItemIndex]);
        FloatAnimation1.Start;
      end
      else
      begin
        Circle1.Position.X := 0;
        Circle1.AnimateFloat(
            'Position.X',
            FLastValue,
            1,
            TAnimationType(cbbAnimation.Items.Objects[cbbAnimation.ItemIndex]),
            TInterpolationType(cbbInterpolation.Items.Objects[cbbInterpolation.ItemIndex])
        );
      end;
    end;
    

    사용방법은 어렵지 않습니다.

    그래도 다양한 효과를 위해서는 TInterpolationType과 TAnimationType을 잘 설정해 주어야 합니다.

    글로 설명하는 것 보다 샘플프로그램 다운로드하셔서 실행해 보시는게 좋을 것같네요.


    샘플프로그램 실행파일 

    FloatAnimation.zip


    샘플프로그램 소스파일

    FloatAnimationSrc.zip


    경고창 애니메이션 효과 소스

    ThAlertAnimation.pas

    Zoom 애니메이션 효과 소스

    ThZoomAnimation.pas


    아래는 엠바카데로의 도움말입니다. 참고하세요.


    http://docwiki.embarcadero.com/Libraries/XE3/en/FMX.Types.TInterpolationType

    Value Meaning

    itLinear

    A linear interpolation. The property value this animation applies to changes constantly over time.

    itQuadratic

    A quadratic function is applied to the path between the start and stop points. The slope of the path is zero at the start point and increases constantly over time. A scalar is applied to the function to make the endpoint fall on the path.

    itCubic

    The interpolation is of the form y = x**3. The slope of the path is zero at the start point and increases much faster than the quadratic function over the path.

    itQuartic

    The interpolation is of the form y = x**4. The slope of the path is zero at the start point and increases much faster than the quadratic function over the path.

    itQuintic

    The interpolation is of the form y = x**5. The slope of the path is zero at the start point and increases much faster than the quadratic function over the path.

    itSinusoidal

    The interpolation is of the form y = sin(x). The slope of the path is zero at the start point and places the first inflexion of the sin curve (x=pi) at the stop point.

    itExponential

    The interpolation is of the form y = e**x. The slope of the path is one at the start point and increase much faster than the quadratic function over the path.

    itCircular

    The path between the start and stop point for this interpolation is a quarter of a circle. The slope of the path is zero at the start point and verticle at the stop point.

    itElastic

    The path does not follow a geometric interpolation. The value (y coordinate) may decrease, moving back toward the Start Value, but time (x value) must always move in a positive direction.

    itBack

    The path does not follow a geometric interpolation. The value (y coordinate) may decrease, moving back toward the Start Value, but time (x value) must always move in a positive direction.

    itBounce

    The path depicts a bouncing ball. The path is made up of circular curves with curvature away from the straight line that connects the start and stop points. These curves are connected by sharp points.


    http://docwiki.embarcadero.com/Libraries/XE3/en/FMX.Types.TAnimationType

    Value Meaning

    atIn

    The curve that applies to the TInterpolationType for this animation starts at the starting value of the property animated.

    atOut

    The curve that applies to the TInterpolationType for this animation starts at the ending value of the property animated and proceeds backwards to the starting value.

    atInOut

    The curve that applies to the TInterpolationType for this animation starts at both the starting value and the ending value of the property animated and meets at the center point.


    저작자 표시 비영리 동일 조건 변경 허락
    신고
    크리에이티브 커먼즈 라이선스
    Creative Commons License

    험프리.김현수 험프리.김현수 파이어몽키 Animation Effect, AnimationFloat, Dynamic UI, Firemonkey, FloatAnimation, FMX, 파이어몽키

    [FM2] XE3.FM2에서 Control을 Bitmap으로 내보내기 및 Bitmap.Pixels 사용하기

    2012.10.04 20:49

    Firemonkey2로 버전업 된 후로 참 많은 것이 변했습니다.


    TControl은 말할 것도 없고 TBitmap도 많은 내용이 변했네요.


    Unit test에서 특정 Pixel의 색상을 얻어오고자 할때 기존 Bitmap.Pixels이 없어진 것을 확인 후 깜짝 놀랐습니다.


    자... 그럼 제가 삽질로 얻어온 내용을 공유하도록 합니다.


    | Control을 Bitmap(TImage)으로 내보내기

    var
      Bitmap: TBitmap;
      Map: TBitmapData;
    begin
      Bitmap :=  TBitmap.Create(Round(Panel1.Width), Round(Panel1.Height));
      try
        Bitmap.Canvas.BeginScene;
    
        // Bitmap으로 내보내기
        Bitmap.Assign(Panel1.MakeScreenshot);
     
        // Pixel의 값을 원하실 때는 이렇게
        Bitmap.Map(TMapAccess.maRead, Map);
        //AlphaColor := Map.GetPixel(Round(X), Round(Y));
        Bitmap.Unmap(Map);
        Bitmap.Canvas.EndScene;
    
        Image1.Bitmap.Assign(Bitmap);
      finally
        Bitmap.Free;
      end;
    end;
    


    Panel1의 화면을 Bitmap으로 내보내는 코드 입니다.

    특정 좌표의 값은 Map.GetPixel로 얻어오실 수 있구요 반환은 TAlphaColor 입니다.


    TBitmapData라는 구조체를 이용하는군요 구글링에도 없는 따끈한 정보입니다. 실은 많이 안찾아 봣습니다. -_-;

    저작자 표시 비영리 동일 조건 변경 허락
    신고
    크리에이티브 커먼즈 라이선스
    Creative Commons License

    험프리.김현수 험프리.김현수 파이어몽키 Bitmap.Pixels, Delphi, Fm2, FMX, TBitmapData, XE3, 태그를 입력해 주세요.

    [FMX] Firemonkey 구조 - 어떻게 하나의 코드로 여러 플랫폼에서 실행 될까?

    2012.06.23 03:04

    이번에는 이리저리 파이어몽키 소스 보며 익혔던 간단한 구조에 대해 설명하려 합니다.

    (딱, 제가 아는 만큼만 소개합니다.^^)


    파이어몽키는 멀티 플랫폼을 지원하는 델파이 프레임웤 입니다.

    어떻게 파이어몽키는 하나의 소스로 여러개의 플랫폼을 지원할까요?

    여러개의 플랫폼을 지원하는 열쇠는 FMX.Platform.pas의 Platform: TPlatform에 있습니다.
    (C:\Program Files (x86)\Embarcadero\RAD Studio\9.0\source\fmx에 소스파일이 있습니다.)


    FMX.Platform의 TPlatform 클래스를 보시면 대부분(거의 다)이 추상메소드(virtual; abstract;) 입니다. 구현이 안되어 있다는 것이죠.

    그리고 메소드명이 상당히 플랫폼에 종속적인 냄새를 풍깁니다.

    CreateWindow(창을 생성하는 건 OS에서 해주겠죠.)
    CreateTimer(타이머도 OS에서 이벤트를 발생해 줍니다.)
    WaitMessage(메시지 처리도 OS와 연관이 있지요.) 등등 모두 플랫폼에 종족적인 메소드들입니다.

    그리고 소스 디렉토리에 보시면 FMX.Platform.Win과 FMX.Platform.Mac 두개의 플랫폼 관련 유닛파일을 보실 수 있습니다.

    즉, FMX.Platform의 TPlatform을 상속받은 클래스들이 구현되어 있겠죠.

    맞습니다. 각 파일을 보시면 TPlatformWin과 TPlatformCocoa 두개의 클래스가 TPlatform을 상속받습니다.

    물론 추상메소드들도 모두 구현되어 있습니다.

    TPlatformWin은 Windows API를 이용하여 구현이 되어 있고, TPlatformCocoa는 OS X API로 구현이 되어 있습니다.


    그럼 어떻게 Firemonkey는 플랫폼에 맞는 TPlatform 객체를 사용할까요?

    위의 질문에 답하기 위해 먼저 프로젝트 소스파일을 들여다 보겠습니다.

    Delphi XE2에서 <Firemonkey HD Application> 프로젝트를 생성하시면 아래와 같은 소스코드를 기본적으로 생성합니다.

    program Project1;
    
    uses
      FMX.Forms,
      Unit1 in 'Unit1.pas' {Form1};
    
    {$R *.res}
    
    begin
      Application.Initialize;
      Application.CreateForm(TForm1, Form1);
      Application.Run;
    end.
    

    위의 프로젝트 소스를 보면 도무지 참조하는 유닛이 FMX.Forms 밖에 없습니다.


    그럼 FMX.Forms 유닛을 살펴보겠습니다.

    주욱 보시면 VCL에서도 익숙한 TApplication, 앞으로 친하게 지내야될 TCommonCustomForm 등등의 클래스 등이 보입니다.

    그리고 제일 아래에 보면 우리가 찾던 Platform 관련된 코드가 보입니다.

    initialization
      RegisterFmxClasses([TApplication], [TApplication]);
      Screen := TScreen.Create(nil);
      Platform := PlatformClass.Create(nil);
      System.Classes.RegisterFindGlobalComponentProc(FindGlobalComponent);
    
    finalization
      System.Classes.UnregisterFindGlobalComponentProc(FindGlobalComponent);
      FreeAndNil(Screen);
      // Platform global is freed in FMX.Types
    

    위 코드의 4번째 줄을 보시면

    Platform := PlatformClass.Create(nil);

    이라고 PlatformClass를 이용해 TPlatform 객체를 생성해 할당합니다.


    자 그럼 다시 PaltformClass를 따라가 봅니다.

    FMX.Platform.pas에 PlatformClass 함수가 구현되어 있습니다.

    function PlatformClass: TPlatformClass;
    begin
      Result := ActualPlatformClass;
    end;

    그럼 ActualPlatformClass는 어디에 구현되어 있을까요?


    예상하시는 분들도 있겠지만 위에서 설명한 Platform별 유닛(FMX.Platform.Win, FMX.Platform.Mac)에 구현되어 있습니다.

    MSWINDOWS의 경우 TPlatformWin을 반환하고

    MACOS의 경우 TPlatformCocoa를 반환합니다.

    근데 의문이 듭니다. 어떻게 델파이는 2개중에 하나를 선택할까요?

    그건 바로 Conditional define($IFDEF)을 이용합니다.

    FMX.Platform의 구현부(implementation) 시작을 보시면 아래와 같은 코드를 보실 수 있습니다.

    {$IFDEF IOS}
    uses
      FMX.Platform.iOS, FMX.Canvas.iOS, FMX.Context.GLES;
    {$ENDIF}
    
    {$IFDEF MACOS}
    uses
      FMX.Platform.Mac, FMX.Canvas.Mac, FMX.Context.Mac;
    {$ENDIF}
    
    {$IFDEF MSWINDOWS}
    uses
      FMX.Platform.Win, FMX.Context.DX9;
    {$ENDIF}
    
    

    보시면 아시겠지만 IOS, MACOS, MSWINDOWS 별로 다른 유닛을 참조하도록 되어있습니다.

    위의 3가지는 모두 Firemonkey가 지원하는 플랫폼입니다.

    즉 Project Manager의 Target platform에서 Platform을 선택하면 그에 맞는 값(IOS, MACOS, MSWINDOWS)이 내부적으로 정의됩니다.

    그래서 결국은 Target platform에 맞는 TPlatform 객체를 이용하여 OS에 맞는 API를 사용하여 실행됩니다.


    좀더 설명을 하자면 위의 프로젝트 소스를 보시면 아래와 같이 Application을 실행(RUN)하는 코드를 볼 수 있습니다.

    Application.Run;

    Run 메소드를 살펴보면 아래와 같이 Platform에 따른 코드를 사용하여 Application을 실행합니다.

    procedure TApplication.Run;
    begin
      FRunning := True;
      AddExitProc(DoneApplication);
      try
        Platform.Run; // Platform의 Run 실행
      finally
        FRunning := False;
      end;
    end;
    
    // MSWINDOWS에 맞게 Application 실행
    procedure TPlatformWin.Run;
    begin
      { checking for canvas }
      if GlobalUseDirect2D then
        SetD2DDefault;
    
      Application.RealCreateForms;
      repeat
        try
          Application.HandleMessage;
        except
          Application.HandleException(Self);
        end;
      until Application.Terminated;
    end;
    
    // MACOS 맞게 Application 실행
    procedure TPlatformCocoa.Run;
    begin
      Application.RealCreateForms;
      CreateApplicationMenu;
      FRunLoopObserver := CFRunLoopObserverCreate(kCFAllocatorDefault, kCFRunLoopBeforeWaiting, True, 0, RunLoopObserverCallback, nil);
      CFRunLoopAddObserver(CFRunLoopGetCurrent, FRunLoopObserver, kCFRunLoopCommonModes);
      NSApp.Run;
    end;
    

    다른 메서드 또는 다른 유닛(FMX.Types 등)에서 Platform 전역변수를 이용하도록 구현되어 있습니다.


    다시한번 정리하면, 

    1, Project 소스에서 FMX.Forms 참조(uses)

    2, FMS.Forms의 Initialization에서 FMS.Platform.pas 유닛의 PlatformClass를 통해 Platform 생성

    3, FMS.Platform는 각 플랫폼 유닛의 ActualPlatformClass 함수 호출하여 Platform에 맞는 TPlatform 객체 형 방ㄴ환

    4, 단, Platform에 맞는 유닛을 $IFDEF를 이용하여 결정($IFDEF는 Target platform 선택 시 정의)


    이상으로 Firemonkey에서 Platform에 맞게 실행되는 구조를 두서없이 설명했습니다.

    감사합니다. 끝~


    PS - 다음에는 Firemonkey를 실질적으로 사용하기 위한 팁들을 몇개 소개하겠습니다.^^

    신고
    크리에이티브 커먼즈 라이선스
    Creative Commons License

    험프리.김현수 험프리.김현수 파이어몽키 Firemonkey, FMX, FMX 구조, multi platform

    [FMX,VCL 비교] #1 VCL의 Canvas와 FMX의 Canvas 차이

    2012.06.20 12:52

    파이어몽키로 작업하다보면 VCL과의 차이점으로 어려움을 겪는 경우가 많습니다.

    그동안 작업하며 습득한 차이점을 한가지한가지 풀어놓으려 합니다.

    그중 첫번째 Canvas 입니다.

    일반적으로 TImage에 Drawing을 하는 예제로 구성하였습니다.
    VCL과 FMX의 동일한 기능을 구현했으니 비교해 보시면 좋을 것 같습니다.

    FMX 코딩시 주의점

    1, TImage.Picture.Bitmap => TImage.Bitmap : VCL의 Picture 객체가 빠졌습니다.

    2, Bitmap.Canvas.BeginScene ~ EndScene, Bitmap.BitmapChange : Canvas에 그리기고 화면에 표시하기 위한 절차입니다.

    3, Brush => Fill, Pen => Stroke 등으로 속성들이 약간씩 변했습니다.

    4, MoveTo, LineTo => DrawLine

    5, FMX의 기본적인 좌표가 Single 형이기 때문에 Bitmap의 Pixel 단위인 Integer로 형변환(Round, Trunc)이 필요합니다.


     이하 소스코드 입니다. 빈프로젝트에 아래 소스 복사하면 컴파일 됩니다.(Unit명만 주의) 폼에 컨트롤이 없습니다.

    | VCL Canvas Freeline Draw

    unit Unit1;
    
    interface
    
    uses
      Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics,
      Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.ExtCtrls;
    
    type
      TForm1 = class(TForm)
        procedure FormCreate(Sender: TObject);
      private
        { Private declarations }
        FDownPos: TPoint;
        FImage: TImage;
    
        procedure MouseDown(Sender: TObject; Button: TMouseButton;
          Shift: TShiftState; X, Y: Integer);
        procedure MouseMove(Sender: TObject; Shift: TShiftState; X, Y: Integer);
      public
        { Public declarations }
      end;
    
    var
      Form1: TForm1;
    
    implementation
    
    {$R *.dfm}
    
    procedure TForm1.FormCreate(Sender: TObject);
    begin
      FImage := TImage.Create(Self);
      FImage.Parent := Self;
      FImage.Align := alClient;
      FImage.OnMouseDown := MouseDown;
      FImage.OnMouseMove := MouseMove;
      FImage.Picture.Bitmap.SetSize(FImage.Width, FImage.Height);
      FIMage.Picture.Bitmap.Canvas.Brush.Color := clBlack;
      FImage.Picture.Bitmap.Canvas.FillRect(FImage.ClientRect);
      FImage.Visible := True;
    end;
    
    procedure TForm1.MouseDown(Sender: TObject; Button: TMouseButton;
      Shift: TShiftState; X, Y: Integer);
    begin
      if ssLeft in Shift then
        FDownPos := Point(X, Y);
    end;
    
    procedure TForm1.MouseMove(Sender: TObject; Shift: TShiftState; X, Y: Integer);
    begin
      if ssLeft in Shift then
      begin
        with FImage.Picture.Bitmap.Canvas do
        begin
          Pen.Color := clRed;
          Pen.Width := 3;
          MoveTo(FDownPos.X, FDownPos.Y);
          LineTo(X, Y);
        end;
    
        FDownPos := Point(X, Y);
      end;
    end;
    
    end.
    


    | FMX Canvas Freeline Draw

    unit Unit2;
    
    interface
    
    uses
      System.SysUtils, System.Types, System.UITypes, System.Classes, System.Variants,
      FMX.Types, FMX.Controls, FMX.Forms, FMX.Dialogs, FMX.Objects;
    
    type
      TForm2 = class(TForm)
        procedure FormCreate(Sender: TObject);
      private
        { Private declarations }
        FDownPos: TPointF;
        FImage: TImage;
    
        procedure MouseDown(Sender: TObject; Button: TMouseButton;
          Shift: TShiftState; X, Y: Single);
        procedure MouseMove(Sender: TObject; Shift: TShiftState; X, Y: Single);
      public
        { Public declarations }
      end;
    
    var
      Form2: TForm2;
    
    implementation
    
    {$R *.fmx}
    
    procedure TForm2.FormCreate(Sender: TObject);
    begin
      FImage := TImage.Create(Self);
      FImage.Parent := Self;
      FImage.Align := TAlignLayout.alClient;
      FImage.OnMouseDown := MouseDown;
      FImage.OnMouseMove := MouseMove;
      FImage.Bitmap.Create(Round(FImage.Width), Round(FImage.Height));
      FImage.Bitmap.Canvas.BeginScene;
      FImage.Bitmap.Canvas.Fill.Color := claBlack;
      FImage.Bitmap.Canvas.FillRect(FImage.ClipRect, 0, 0, AllCorners, 1);
      FImage.Bitmap.Canvas.EndScene;
      FImage.Visible := True;
    
    end;
    
    procedure TForm2.MouseDown(Sender: TObject; Button: TMouseButton;
      Shift: TShiftState; X, Y: Single);
    begin
      if ssLeft in Shift then
        FDownPos := PointF(X, Y);
    end;
    
    procedure TForm2.MouseMove(Sender: TObject; Shift: TShiftState; X, Y: Single);
    begin
      if ssLeft in Shift then
      begin
        with FImage.Bitmap.Canvas do
        begin
          BeginScene;
          Stroke.Color := claRed;
          StrokeThickness := 3;
          DrawLine(FDownPos, PointF(X, Y), 1);
          EndScene;
        end;
        FImage.Bitmap.BitmapChanged;
    
        FDownPos := PointF(X, Y);
      end;
    end;
    
    end.
    



    신고
    크리에이티브 커먼즈 라이선스
    Creative Commons License

    험프리.김현수 험프리.김현수 파이어몽키 Canvas, Firemonkey, FMX, FMX VCL 차이, 파이어몽키