FireMonkey Programming Forum
C++Builder  |  Delphi  |  FireMonkey  |  C/C++  |  Free Pascal  |  Firebird
볼랜드포럼 BorlandForum
 경고! 게시물 작성자의 사전 허락없는 메일주소 추출행위 절대 금지
FireMonkey 포럼
Q & A
FAQ
팁&트릭
강좌/문서
자료실
볼랜드포럼 홈
헤드라인 뉴스
IT 뉴스
공지사항
자유게시판
해피 브레이크
공동 프로젝트
구인/구직
회원 장터
건의사항
운영진 게시판
회원 메뉴
북마크
델마당
볼랜드포럼 광고 모집

FireMonkey Q&A
[201] Re: 안드로이드 FMX IME 문자조합 질문이죠?
빌더(TWx) [builder] 1890 읽음    2019-09-05 15:23
100jk 님이 쓰신 글 :
: 구글검색 중에 나오는 팁들은 전부 윈도우 기반이네요
:
: FMX에서 IME조합중인 문자를 가져오는 방법은 없을까요?



답변:


Java로 프로그래밍 하면...
안드로이드 Input Method 클래스나, 키보드 Connection 클래스를 바인드해서
간단하게 처리할 수 있지만...

FMX 파이어몽키로 처리하려면 삽질을 해야 합니다.

FMX 프레임웍이 만들어질 때... 기존 윈도우즈 플렛폼에서의 VCL 프레임웍 랜더링을
DirectX로 대신하는 구조로 설계됐던 건데...

VCL 프레임웍의 TEdit, TListBox, TMemo 와 같은 상위 레이어 컴포넌트를 베이스로 해서
안드로이드, Mac, iOS, 윈도우즈... 각기 플렛폼을 one source multi platform을 지원한답시고

legacy 아키텍쳐에 온갖 프록시 클래스를 섞어 놓아서...
지금의 멀티디바이스 FMX 프레임웍이 만들어졌기 때문에 프레임웍이 복잡하고 지져분하게 구성되어 있기 때문 입니다.



델파이 컴파일러가 FMX 프레임웍을 만들면서 ARC로 닭짓을 해놨기 때문에...
FMX 프레임웍 ARC 레벨에서 C++ 컴파일러를 이용해서 작업할 수 없어서
코드는 파스칼로 구현하는 거로 대신 합니다.

rad studio 10.3.2 기준으로 구현. (10.3 으로 넘어 오면서 FMX 프록시 클래스 구조가 또 바꼈음.)

C++ 컴파일러로 작업할려면 델파이 FMX 프레임웍 ARC 메모리 모델이 폐기 되어야 함.

<델파이 FMX ARC 메모리 모델의 문제점>
http://delphi.borlandforum.com/impboard/impboard.dll?action=read&db=del_qna&no=16034


unit Unit2;

interface

implementation

uses
  FMX.Forms, FMX.Platform.Android, FMX.Platform,

  FMX.Controls, FMX.Controls.Presentation,
  FMX.Presentation.Android.Style, FMX.Text,
  AndroidApi.Helpers, System.Messaging,

  System.SysUtils, System.Types,
  Androidapi.JNI.GraphicsContentViewText, Androidapi.JNI.Embarcadero,
  FMX.Types, FMX.VirtualKeyboard, Androidapi.JNIBridge,
  Androidapi.JNI.JavaTypes;


type
  KeyboardAndroidEx = class;

  VKListener = class(TJavaLocal, JOnKeyboardStateChangedListener)
  private
    [weak] FKeyboardService: KeyboardAndroidEx;
    FNeedNotify: Boolean;

  public
    constructor Create(Svc: KeyboardAndroidEx);
    procedure onVirtualKeyboardFrameChanged(newFrame: JRect); cdecl;
    procedure onVirtualKeyboardWillShown; cdecl;
    procedure onVirtualKeyboardWillHidden; cdecl;
  end;

  FMXListenerEx = class(TJavaLocal, JFMXTextListener)
  strict private
    FProcessed: boolean;
  public
    constructor Create(const Service: KeyboardAndroidEx); overload;
    procedure onEditorAction(aCode: Integer); cdecl;
    procedure onTextUpdated(cs: JCharSequence; pos: Integer); cdecl;
    procedure onComposingText(bPos: Integer; ePos: Integer); cdecl;
    procedure onSkipKeyEvent(event: JKeyEvent); cdecl;

  end;

  KeyboardAndroidEx = class(TInterfacedObject, IFMXVirtualKeyboardService)
  private
    FVKListener: VKListener;
    FTransient: Boolean;
    FTextListener: FMXListenerEx;

    function IsAutoShow: Boolean;
    procedure Register;
  public
    constructor Create;
    destructor Destroy; override;

  protected
    function DefineView(const AObject: TFmxObject): JView;
    procedure NotificationKeyboardEvent(const AVKRect: TRect);

    function ShowVirtualKeyboard(const AControl: TFmxObject): Boolean;
    function HideVirtualKeyboard: Boolean;
    function GetVirtualKeyBoardState: TVirtualKeyBoardStates;
    procedure SetTransientState(Value: Boolean);
    property VirtualKeyBoardState: TVirtualKeyBoardStates read GetVirtualKeyBoardState;
  end;


constructor KeyboardAndroidEx.Create;
begin
  inherited Create;
end;

destructor KeyboardAndroidEx.Destroy;
begin
  inherited;
end;

function KeyboardAndroidEx.DefineView(const AObject: TFmxObject): JView;

  function IsControl: Boolean;
  begin
    Result := (AObject is TPresentedControl) and (TPresentedControl(AObject).ControlType = TControlType.Platform);
  end;

  function GetView: JView;
  begin
    Result := JView(TPresentedControl(AObject).PresentationProxy.NativeObject);
  end;

  function GetFormView: JView;
  begin
    Result := WindowHandleToPlatform(TCommonCustomForm(TControl(AObject).Root.GetObject).Handle).View;
  end;

  function IsStyledControl: Boolean;
  begin
    Result := (AObject is TPresentedControl) and (TPresentedControl(AObject).Presentation is TAndroidStyledPresentation);
  end;

  function IsAvailable: Boolean;
  begin
    Result := (AObject is TControl) and (TControl(AObject).Root <> nil) and
              TCommonCustomForm(TControl(AObject).Root.GetObject).IsHandleAllocated;
  end;

begin
  if IsStyledControl and IsAvailable then
    Result := GetFormView
  else if IsControl then
    Result := GetView
  else if Supports(AObject, ITextInput) then
    Result := MainActivity.getEditText
  else if IsAvailable then
    Result := GetFormView
  else
    Result := nil;
end;

function KeyboardAndroidEx.IsAutoShow: Boolean;
begin
  Result := VKAutoShowMode in [TVKAutoShowMode.Always, TVKAutoShowMode.DefinedBySystem];
end;

procedure KeyboardAndroidEx.Register;
begin
  if not Assigned(FVKListener) then
  begin
    FVKListener := VKListener.Create(Self);
    MainActivity.getVirtualKeyboard.addOnKeyboardStateChangedListener(FVKListener);
  end;
end;

function KeyboardAndroidEx.GetVirtualKeyBoardState: TVirtualKeyBoardStates;
begin
  Result := [];
  if IsAutoShow then
    Include(Result, TVirtualKeyboardState.AutoShow);
  if FTransient then
    Include(Result, TVirtualKeyboardState.Transient);
  if MainActivity.getVirtualKeyboard.isVirtualKeyboardShown then
    Include(Result, TVirtualKeyboardState.Visible);

end;

function KeyboardAndroidEx.HideVirtualKeyboard: Boolean;
begin
  Result := False;
  try
    if not FTransient then
    begin
      Register;
      MainActivity.getEditText().removeTextListener(FTextListener);
      Result := MainActivity.getVirtualKeyboard.hide;
    end;
  except
    Application.HandleException(Screen.ActiveForm);
  end;
end;


procedure KeyboardAndroidEx.NotificationKeyboardEvent(const AVKRect: TRect);
var
  M: TVKStateChangeMessage;
begin
  M := TVKStateChangeMessage.Create(TVirtualKeyboardState.Visible in VirtualKeyboardState, AVKRect);
  TMessageManager.DefaultManager.SendMessage(Self, M, True);
end;

function KeyboardAndroidEx.ShowVirtualKeyboard(const AControl: TFmxObject): Boolean;
  function IsNotFocused(const AControl: TFmxObject): Boolean;
  begin
    Result := (AControl is TControl) and not TControl(AControl).IsFocused;
  end;

begin
  if IsNotFocused(AControl) then
    TControl(AControl).SetFocus;
  Register;
  if not Assigned(FTextListener) then
    FTextListener := FMXListenerEx.Create(Self);

  MainActivity.getEditText().addTextListener(FTextListener);
  Result := MainActivity.getVirtualKeyboard.showFor(DefineView(AControl));
end;


procedure KeyboardAndroidEx.SetTransientState(Value: Boolean);
begin
  FTransient := Value;
end;


constructor VKListener.Create(Svc: KeyboardAndroidEx);
begin
  inherited Create;
  FKeyboardService := Svc;
end;

procedure VKListener.onVirtualKeyboardFrameChanged(newFrame: JRect);
begin
end;

procedure VKListener.onVirtualKeyboardWillShown;
begin
  FNeedNotify := FNeedNotify or
     not (TVirtualKeyboardState.Visible in FKeyboardService.VirtualKeyboardState);
end;

procedure VKListener.onVirtualKeyboardWillHidden;
begin
  FNeedNotify := FNeedNotify or
     (TVirtualKeyboardState.Visible in FKeyboardService.VirtualKeyboardState);
end;



constructor FMXListenerEx.Create(const Service: KeyboardAndroidEx);
begin
  inherited Create;
end;

procedure FMXListenerEx.onComposingText(bPos, ePos: Integer);
begin
end;

procedure FMXListenerEx.onSkipKeyEvent(event: JKeyEvent);
begin
end;

procedure FMXListenerEx.onTextUpdated(cs: JCharSequence; pos: Integer);
begin
  if pos = 0 then
    Exit;

  if not FProcessed then
  begin
    // 디버깅덤프. 문자조합과정 출력
    Log.d(Format('%s', [JCharSequenceToStr(cs)[pos-1]]));
    FProcessed := True;
  end
  else
    FProcessed := False;
end;

procedure FMXListenerEx.onEditorAction(aCode: Integer);
begin
end;


var
  FKeyboardAndroidEx: KeyboardAndroidEx = nil;
  FVKService: IFMXVirtualKeyboardService = nil;

initialization
  FKeyboardAndroidEx := KeyboardAndroidEx.Create;
  FVKService := IFMXVirtualKeyboardService(TPlatformServices.Current.GetPlatformService(IFMXVirtualKeyboardService));
  if FVKService <> nil then
    TPlatformServices.Current.RemovePlatformService(IFMXVirtualKeyboardService);
  TPlatformServices.Current.AddPlatformService(IFMXVirtualKeyboardService, FKeyboardAndroidEx);

end.







메인폼에 TEdit 올려놓고...
위의 Unit2를 메인 유닛의 implementaion 섹션의 uses 에 포함해서 컴파일만 하면 됌.

컴파일 후... TEdit에 "국가"를 입력하면 아래 캡쳐 화면과 같이...
LogCat을 통해서 문자조합 과정이 출력될 겁니다. (LogCat을 이용한 디버깅 덤프방법은 생략함)






간단한가요?

one source multi platform은 초보자들 꼬득이기 위한 상술에 불과한 거고...
Java와 Android sdk 프레임웍 구조 모르면... 기술적인 문제에 부딛혔을 때... 아무것도 손 못댑니다.

Java와 Android sdk 프레임웍 구조부터 마스터 하세요.
이걸 마스터 해놔야...

Android 프레임웍과 비슷한 구조로 만들어져 있는 C# Xamarin도 마음대로 요리할 수 있게 됍니다.

C# Xamarin으로 작성된 코드는 델파이 보다 느리다는 황당한 궤변을 늘어놓는 사람들도 있던데...
아마도 이런 사람들은 Android 프레임웍 구조도 모르고, C# 도 디테일하게 잘 모르고 있으면서
어설프게 델파이 하나만 맹신적으로 사용하고 있는 사람일 겁니다.




+ -

관련 글 리스트
200 IME 조합중인 문자가져오는법?? 100jk 1274 2019/08/31
201     Re: 안드로이드 FMX IME 문자조합 질문이죠? 빌더(TWx) 1890 2019/09/05
202         Re:Re: 안드로이드 FMX IME 문자조합 질문이죠? 100jk 1547 2019/09/12
Google
Copyright © 1999-2015, borlandforum.com. All right reserved.