Search results for '파이어몽키'

  1. 2017.09.20 -- [FMX] TListView 더보기 버튼 구현하기
  2. 2017.09.12 -- [FMX] 폼의 높이를 애니메이션으로 변경하기, 툴바를 끌어 폼 이동하기
  3. 2017.09.08 -- TListView 행별로 배경색 설정 및 글꼴변경하기
  4. 2017.04.20 -- 델파이 앱에서 움직이는 GIF 표현하기(소스 포함)
  5. 2017.03.20 -- 블루투스 바코드 스캐너(BI-07) 안드로이드 라이브러리를 델파이에서 연동하는 방법 (16)
  6. 2016.12.09 -- [안드로이드] 포토 라이브러리 목록 불러오기
  7. 2016.09.22 -- 모바일 디바이스 화면 방향전환 이벤트 수신하기
  8. 2016.09.20 -- TListView Dynamic Appearance로 추가한 항목을 코드로 제어하기 (1)
  9. 2016.07.28 -- 구글 통계(Google Analytics)를 써드파티 컴포넌트 없이 구현하는 방법
  10. 2016.04.27 -- [게임] 매의 눈(컵속에 숨겨진 주사위 찾기 게임)
  11. 2016.04.04 -- [OSX/맥] 맥의 스크린세이버 비활성화/활성화 시키기 (2)
  12. 2016.04.04 -- [FMX] 앱에서 문장을 출력하는 3가지 방법
  13. 2016.03.31 -- [안드로이드/iOS] 이미 배포(Deployment)된 파일 업데이트 하기
  14. 2016.01.28 -- 델파이 iOS Touch ID 구현하기(래퍼 클래스) (1)
  15. 2015.11.04 -- [데이터스냅] 데이터스냅 클라이언트에서 TCP/IP로 접속 시 ConnectTimeout이 적용되지 않는 경우 대처방법 (1)
  16. 2015.10.02 -- [따라하기] 건강데이터 수집 및 기록 시스템 #1 - BLE 기반 스마트 체중계에서 실시간 데이터 받기 (11)
  17. 2015.10.01 -- iOS9의 새로운 기능인 "App transport Security" 예외 허용을 위한 Info.plist xml 수정방법
  18. 2015.07.28 -- [따라하기] 위험지역 경보 시스템 #1 - 비콘을 이용해 위험지역 진입 경보앱 만들기 (19)
  19. 2015.07.27 -- [XE8] 모바일 앱에서 클래식 블루투스를 이용해 데이터 전송하기 (2)
  20. 2015.07.02 -- 모바일 앱 실행 시 검은화면이 표시되는 경우 조치방법

[FMX] TListView 더보기 버튼 구현하기

2017.09.20 13:49

모바일 앱을 개발할 때에 리스트 항목이 많은 경우, 한번에 가져오면 시간이 오래 걸려 좋지 않은 사용자 경험이 됩니다.


이 경우, 리스트 중 일부를 빠르게 표시하고, 리스트 끝으로 이동 시 추가로 표시하는 방식을 통해 문제를 해결할 수 있습니다.


이 글에서는, 

리스트뷰(TListView)의 목록 끝으로 이동 시, 더보기 버튼을 표시하도록 (매우 심플하게)구현합니다.

버튼 클릭 시 항목을 추가합니다.


이 글을 참고해 리스트 끝으로 이동 시 자동으로 항목을 추가하도록 구현할 수도 있습니다.


리스트뷰 더보기 버튼  구현하기

결과

결과 화면은 다음과 같습니다.


처음 24개 항목 표시 후, 목록 끝으로 이동 시 버튼이 표시되고, 버튼을 눌러 항목을 추가합니다.


구현방법

1) 더보기 버튼을 리스트뷰(TListView)에 올려놓고 감춥니다.(Visible := False)

2) 리스트뷰의 스크롤 이동(OnScrollViewChange) 이벤트 핸들러에서 제일 끝으로 이동한 경우 버튼을 표시합니다.


컴포넌트 구성은 아래와 같습니다.


구현된 주요 코드는 아래와 같습니다.

procedure TForm2.AddItem(ACount: Integer);
var
  I: Integer;
  Item: TListViewItem;
begin
  for I := 0 to ACOunt - 1 do
  begin
    Item := ListView1.Items.Add;
    Item.Text := 'Item ' + ListView1.Items.Count.ToString;
  end;
end;

procedure TForm2.FormCreate(Sender: TObject);
begin
  AddItem(24);
  btnListViewMore.Visible := False;
end;

procedure TForm2.btnListViewMoreClick(Sender: TObject);
begin
  AddItem(6);
  btnListViewMore.Visible := False;
end;

procedure TForm2.ListView1Resized(Sender: TObject);
begin
  ListView1ScrollViewChange(Sender);
end;

procedure TForm2.ListView1ScrollViewChange(Sender: TObject);
var
  LastItemBottom, ListViewBottom: Single;
begin
  LastItemBottom := ListView1.GetItemRect(ListView1.ItemCount - 1).Bottom;
  ListViewBottom := ListView1.LocalRect.Bottom;

  btnListViewMore.Visible := ((LastItemBottom - ListViewBottom) < 30);
end;


샘플 프로젝트

ListViewMoreButton.zip


이 샘플에서는 더보기 버튼 클릭 시 단순하게 목록을 추가했습니다. 실무에서는 더보기 버튼 클릭시 추가 데이터를 수신 후 목록을 동적으로 갱신하도록 구현해야 합니다.


당겨서 새로고침(Pull to refresh) 구현하기

모바일 앱에서는 새로운 정보를 가져오기 위해 당겨서 새로고침 기능을 사용할 수 있습니다.

이 기능은 리스트뷰(TListView)의 속성을 이용해 구현할 수 있습니다.


PullToRefresh 속성을 True로 설정 시, 목록 상단에서 당겨서 새로고침 기능을 제공합니다.

위 동작이 발생하면 OnPullRefresh 이벤트가 발생합니다.


참고링크


저작자 표시 비영리 변경 금지
신고

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

[FMX] 폼의 높이를 애니메이션으로 변경하기, 툴바를 끌어 폼 이동하기

2017.09.12 15:59

약국용 라디오 어플을 개발하는 개발사에서는, 오래전부터 PC 기반으로만 라디오 서비스를 하고 있습니다.

해당 개발사에서는 노후된 앱을 최신 운영체제(윈도우 10 등) 지원과 모바일 확장을 위해 파이어몽키로 재개발 중입니다.

(파이어몽키는 윈도우, OSX, iOS, 안드로이드를 하나의 소스코드로 개발할 수 있습니다.)


개발 중 몇가지 질문을 주셔서 기술지원 후 해당 내용 일부를 공유합니다.


이 글에서는 다음 내용의 구현 방법을 소개합니다.

1, 폼의 일부영역 보이기/가리기(Collapse)

2, Border가 없는 폼에서 특정영역(Toolbar)을 마우스로 이동하기


결과 화면은 아래와 같습니다.

1, 폼의 일부영역 보이기/가리기(Collapse)

이 기능은 TFloatAnimation 컴포넌트를 사용해 손쉽게 구현할 수 있었습니다.

(파이어몽키에는 애니메이션과 효과를 기본 컴포넌트로 제공합니다.)


TFloatAnimation은 지정 된 시작과 끝 Float 값에 대해 애니메이션 되면 값이 변경할 수 있어, 크기, 위치, 각도, 투명도 등을 천천히 변경할 수 있습니다.


하지만, 폼의 높이(Height) 속성에는 FloatAnimaion을 지정할 수 없어, 폼에 레이아웃 컴포넌트를 놓고, 레이아웃 컴포넌트의 높이를 변경시키고, 변경 시 발생하는 OnProcess 이벤트에서 폼의 높이를 조정하도록 구현했습니다.


위 기능의 코드는 아래와 같습니다.

procedure TForm2.FormCreate(Sender: TObject);
begin
  Self.BorderStyle := TFmxFormBorderStyle.None;

  FOrgHeight := lytMain.Height;
end;

procedure TForm2.Button1Click(Sender: TObject);
begin
  lytMain.Align := TAlignLayout.None;
  FloatAnimation1.Duration := 0.5;
  if lytMain.Height = 0  then
  begin
    FloatAnimation1.StartValue := 0;
    FloatAnimation1.StopValue := FOrgHeight;
    FloatAnimation1.Start;
  end
  else
  begin
    FOrgHeight := lytMain.Height;
    FloatAnimation1.StartValue := FOrgHeight;
    FloatAnimation1.StopValue := 0;
    FloatAnimation1.Start;
  end;
end;

procedure TForm2.FloatAnimation1Finish(Sender: TObject);
begin
  lytMain.Align := TAlignLayout.Client;
end;

procedure TForm2.FloatAnimation1Process(Sender: TObject);
begin
  // 레이아웃 크기 조정 시 폼의 크기 조정
  Self.Height := Trunc(ToolBar1.Height + lytMain.Height) + 1;
end;


2, Border가 없는 폼에서 특정영역을 마우스로 이동하기

테두리가 없는 폼에서 제목등의 특정영역을 마우스로 끌어 이동하기 위해 다음 코드를 사용했습니다.

(해당 코드는 윈도우와 OSX(맥)에서 모두 동작합니다.)

procedure TForm2.ToolBar1MouseDown(Sender: TObject; Button: TMouseButton;
  Shift: TShiftState; X, Y: Single);
begin
  if (Button = TMouseButton.mbLeft) then StartWindowDrag;
end;




저작자 표시 비영리 변경 금지
신고

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

TListView 행별로 배경색 설정 및 글꼴변경하기

2017.09.08 10:56

리스트뷰(TListView)는 많은 데이터를 목록으로 표시하기 위한 컴포넌트입니다.

스와이프(스크롤) 속도가 빠른 대신 항목 구성하기가 까다롭습니다.


이번 글에서는 리스트뷰 표현 방식 중 DynamicAppearance(동적 외관구성)를 이용해 외관 구성 후, 아래 기능을 구현했습니다.

1) 표시 항목을 추가(현재 2개, 여러 항목 표시가능)

2) 조건에 따라 배경색상을 변경

3) 조건에 따라 글꼴 변경


결과 화면은 아래와 같습니다.

(아이폰에서는 "여자" 글꼴을 Bold로 주니 글자가 약간 커지네요.)



구현한 내용은 아래와 같습니다.

화면구성

대표 컴포넌트는 리스트뷰(TListView)와 이미지리스트(TImageList)입니다.

이미지 리스트에는 2개의 색상 이미지(16x16)를 추가했습니다.


리스트뷰의 ItemAppearance.ItemAppearance를 DynamicAppearance로 설정했습니다.


아이템 오브젝트는 문자를 표현할 Text 항목 2개와 배경을 표현할 Image 항목 총 3개로 구성했습니다.

(Image 항목을 추가한 이유는 Rectangle과 같은 도형을 지원했다면 도형의 배경으로 처리했겠지만, 지원하지 않아 이미지를 이용해 배경으로 처리했습니다.)


코드작성

코드의 주요 기능은 2가지 입니다.

1) 리스트뷰 아이템(TListViewItem)의 값에 따라 배경색 및 글꼴 설정

2) 리스트뷰 크기 변경(OnResized 이벤트) 시 항목너비 조정


참고로 리스트뷰 아이템 서식 변경의 경우 OnUpdateObjects 이벤트에서 처리했지만, 

코드로 목록을 추가하는 경우, 추가하는 시점에 서식을 설정할 수도 있습니다.

procedure TForm1.FormCreate(Sender: TObject);
begin
  ListView1.ItemSpaces := TBounds.Create(RectF(0, 0, 0, 0));
end;

procedure TForm1.Button1Click(Sender: TObject);
var
  Item: TListViewitem;
begin
  Item := ListView1.Items.Add;
  Item.Data['Text1'] := '김현수';
  Item.Data['Text2'] := '남자';
end;

procedure TForm1.Button2Click(Sender: TObject);
var
  Item: TListViewitem;
begin
  Item := ListView1.Items.Add;
  Item.Data['Text1'] := '아이유';
  Item.Data['Text2'] := '여자';
//  (Item.Objects.ObjectByName('Image2') as TListItemImage).Bitmap := ImageList1.Bitmap(TSizeF.Create(16, 16), 1);
end;

function FindTextObject(const AListView: TListView; AName: string): TCommonObjectAppearance;
var
  Obj: TCommonObjectAppearance;
begin
  Result := nil;

  for Obj in AListView.ItemAppearanceObjects.ItemObjects.Objects do
  begin
    if Obj.Name = AName then
      Exit(Obj as TCustomTextObjectAppearance);
  end;
end;

procedure TForm1.ListView1Resized(Sender: TObject);
var
  Text1, Text2: TCommonObjectAppearance;
begin
  Text1 := FindTextObject(ListView1, 'Text1');
  Text2 := FindTextObject(ListView1, 'Text2');

  Text1.Width := ListView1.Width / 2;
  Text2.PlaceOffset.X := Text1.Width;
  Text2.Width := ListView1.Width / 2;
end;

procedure TForm1.ListView1UpdateObjects(const Sender: TObject;
  const AItem: TListViewItem);
var
  ImageItem: TListItemImage;
  TextItem: TListItemText;
begin
  ImageItem := AItem.Objects.DrawableByName('Image2') as TListItemImage;
  TextItem := AItem.Objects.DrawableByName('Text2') as TListItemText;

  if Assigned(ImageItem) then
  begin
    if AItem.Data['Text1'].ToString = '김현수' then
      ImageItem.Bitmap := ImageList1.Bitmap(TSizeF.Create(16, 16), 0)
    else
      ImageItem.Bitmap := ImageList1.Bitmap(TSizeF.Create(16, 16), 1)
    ;
    ImageItem.Visible := True;
  end;

  if Assigned(TextItem) then
  begin
    if Textitem.Text = '여자' then
      Textitem.Font.Style := [TFontStyle.fsBold, TFontStyle.fsItalic];
  end;
end;

샘플 프로젝트


위 구성은 리스트뷰 항목별로 배경과 글꼴을 설정했습니다.

위 내용을 참고하면, 각 항목의 위치, 크기 등을 자유자제로 편집할 수 있어 활용도는 무궁무진할 것으로 보입니다.


좋은 아이디어가 있다면, 저에게도 알려주셔서 공유 부탁드립니다.


참고자료


델파이 기본샘플을 적극 참고했습니다.


ListView 관련 샘픙은 기본 설치 경로 기준 아래와 같습니다.(10.2 도쿄 기준)
C:\Users\Public\Documents\Embarcadero\Studio\19.0\Samples\Object Pascal\Multi-Device Samples\User Interface\ListView


다음과 같은 샘플들이 있습니다.

그중 CustomAdapter와 ListViewImageIndex 샘플을 많이 참고했습니다.


CustomAdapter

웹상의 이미지를 표시하고, 상단에 큼지막한 이름과 하단에 설명과 버튼을 구성합니다.

목록의 크기를 이미지에 맞게 조정합니다.



ListViewImageIndex

리스트뷰와 데이터셋을 라이브바인딩과 코드를 이용해 목록을 구성합니다.

이미지는 이미지리스트를 활용합니다.


기타 다른 샘플들도 참고할 만한 내용이 많습니다. 다른 샘플들은 직접 둘러보시고 도움을 받으시기 바랍니다.

저작자 표시 비영리 변경 금지
신고

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

델파이 앱에서 움직이는 GIF 표현하기(소스 포함)

2017.04.20 10:20

한 중국인 델파이 개발자 블로그(武稀松(wr960204)的博客)에서 움직이는 GIF를 파이어몽키에서 사용할 수 있는 소스코드가 공개되어 공유합니다.



위 GIF는 샘플 프로젝트로 돌려본 화면입니다. 

첫번째 버튼은 GIF 파일 로드, 두번째 버튼으로 시작/종료 입니다. 세번째, 네번째는 느리게 빠르게 동작하는 버튼입니다.


이 소스코드는 윈도우32, 윈도우64, 안드로이드 그리고 리눅스까지 지원합니다.

더 자세한 내용(중국어)과 소스코드 다운로드는 아래 링크를 참고하세요.


저작자 표시 비영리 동일 조건 변경 허락
신고

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

블루투스 바코드 스캐너(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의 댓글을 참고하거나 등록해주세요.

저작자 표시 비영리 동일 조건 변경 허락
신고

험프리.김현수 파이어몽키 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 과 비슷한 내용만 들어가 있습니다. 파일도 엄청 크고요...
    뭐가 문제인지 삽질 중입니다.
    고견 있으시면 댓글 부탁드립니다.
    감사합니다.

  16. 데브기어 테크게시판(tech.devgear.co.kr)의 개발자 Q&A에 해당 jar파일과 함께 다시 등록해 주세요.

    잘될지는 모르겠지만 저도 시도해보도록 하겠습니다.

[안드로이드] 포토 라이브러리 목록 불러오기

2016.12.09 17:15



안드로이드 내부 저장소에 저장된 사진의 썸네일 목록을 가져오는 코드입니다.


uses
  Androidapi.Helpers, Androidapi.JNI.Net, Androidapi.JNIBridge,
  Androidapi.JNI.Provider, Androidapi.JNI.GraphicsContentViewText,
  FMX.Helpers.Android, Androidapi.JNI.JavaTypes;


procedure TForm1.Button1Click(Sender: TObject);
var
  Uri: Jnet_Uri;
  Projection: TJavaObjectArray<jstring>;
  Cursor: JCursor;
  FilePath: string;
  Item: TListBoxItem;
begin
  Uri := TJImages_Thumbnails.JavaClass.EXTERNAL_CONTENT_URI;
  Projection := TJavaObjectArray<jstring>.Create(1);
  Projection[0] := TJImages_Thumbnails.JavaClass.DATA;

  Cursor := TAndroidHelper.ContentResolver.query(Uri, Projection, nil, nil, nil);

  if not  Assigned(Cursor) then
    Exit;

  try
    Cursor.moveToFirst;
    while(Cursor.moveToNext) do
    begin
      FilePath := JStringToString(Cursor.getString(Cursor.getColumnIndex(TJImages_Thumbnails.JavaClass.DATA)));

      Item := TListBoxItem.Create(ListBox1);
      Item.Parent := ListBox1;
      Item.Text := FilePath;
      Item.ItemData.Bitmap.LoadFromFile(Filepath);
      // 쓰레드로 처리해야 함(로딩이 오래 걸림)
    end;
  finally
    Cursor.close;
  end;
end;

자세한 설명은 추가하겠습니다.


저작자 표시 비영리 동일 조건 변경 허락
신고

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

모바일 디바이스 화면 방향전환 이벤트 수신하기

2016.09.22 10:23

모바일 디바이스의 방향을 가로 또는 세로, 그리고 역방향으로 돌리는 동작을 알아낼 수 있습니다.


파이어몽키 프레임워크에서 제공하는 기능으로 TMessageManager에 구독해 이벤트를 받을 수 있습니다.

(부가적으로 이런 개발 방식을 옵저버 패턴이라고 합니다.^^)



위 스크린샷은 

1) 시작(폼생성) 시 TMessageMenager에 TOrientationChageMessage에 대해 OrientationChaged 이벤트를 구독하고

2) 종료 시 : 구독을 해지합니다.

3) 현재 스크린 방향은 IFMXScreenService.GetScreenOrientation 메소드를 통해 알아 올 수 있습니다.


자세한 내용은 아래 샘플코드를 통해 확인하세요.

델파이 10.1 베를린 버전으로 작성되었습니다.

ScreenOrientation-Berlin.zip




해당 내용은 델파이 부트캠프 - 델파이 게임 개발 소스코드를 살펴보다 발견했습니다.

델파이 부트캠프: 게임 개발 - http://community.embarcadero.com/blogs?view=entry&id=8785

델파이 게임개발 소스코드(Github) : https://github.com/EmbarcaderoPublic/DelphiArcadeGames

저작자 표시 비영리 동일 조건 변경 허락
신고

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

TListView Dynamic Appearance로 추가한 항목을 코드로 제어하기

2016.09.20 15:16

(해당 기능은 RAD 스튜디오 10.1 베를린 이상에서 사용할 수 있습니다.)


TListView는 이미 정의된 ItemAppreance를 이용해 목록을 구성하기 때문에 TListBox에 비해 스크롤이 부드럽고 빠릅니다.


하지만, 베를린 버전에서 Dynamic appreance 기능이 추가되어 아래 그림과 같이 아이템 항목을 마음대로 정의할 수 있습니다.



Dynamic Appreance에 대한 자세한 내용은 아래 링크를 참고하세요.

오늘 소개할 내용은 위와 같이 사용자가 추가한 항목을 코드를 통해 제어하는 예제입니다.


아래 코드는 ListView1에서 Text2 아이템을 찾아 글자색을 녹색으로, 글자 스타일을 Bold, Italic으로 설정합니다.

procedure TForm2.Button3Click(Sender: TObject);
  function GetTextItem2: TCustomTextObjectAppearance;
  var
    Obj: TCommonObjectAppearance;
  begin
    Result := nil;
    for Obj in ListView1.ItemAppearanceObjects.ItemObjects.Objects do
    begin
      if not (Obj is TCustomTextObjectAppearance) then
        Continue;

      if Obj.Name = 'Text2' then
      begin
        Result := Obj as TCustomTextObjectAppearance;
        Break;
      end;
    end;
  end;
var
  Text2: TCustomTextObjectAppearance;
begin
  // Text2 항목 찾기
  Text2 := GetTextItem2;

  if not Assigned(Text2) then
    Exit;

  Text2.TextColor := TAlphaColorRec.Green;
  Text2.Font.Style := [TFontStyle.fsBold, TFontStyle.fsItalic];
//  Text2.re
end;


아래 코드는 ListView의 크기가 변경된 경우(TListView.OnResize) 내부 항목들의 위치와 너비를 일괄조정하는 샘플입니다.

procedure TForm2.ListView1Resize(Sender: TObject);
var
  L, R, W, Ratio: Single;
  Obj: TCommonObjectAppearance;
begin
  W := ListView1.Width - ListView1.ItemSpaces.Left - ListView1.ItemSpaces.Right;
  L := W;
  R := 0;

  for Obj in ListView1.ItemAppearanceObjects.ItemObjects.Objects do
  begin
    // 가장 왼쪽의 이미지 제외
    if Obj is TCustomImageObjectAppearance then
      Continue;

    // 가장 왼쪽 위치
    if Obj.PlaceOffset.X < L then
      L := Obj.PlaceOffset.X;
    // 가장 오른쪽 위치
    if (Obj.PlaceOffset.X + Obj.Width) > R then
      R := Obj.PlaceOffset.X + Obj.Width;
  end;

  Ratio := (W-L) / (R-L); // (변경된 너비 / 원래너비)
  for Obj in ListView1.ItemAppearanceObjects.ItemObjects.Objects do
  begin
//    if Obj.PlaceOffset.X < L then
    if Obj is TCustomImageObjectAppearance then
      Continue;

    Obj.PlaceOffset.X := ((Obj.PlaceOffset.X - L)  * Ratio) + L;
    Obj.Width := Obj.Width * Ratio;
  end;
end;


샘플 프로젝트

ListViewDynamicAppearance.zip


저작자 표시 비영리 동일 조건 변경 허락
신고

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

  1. Blog Icon
    채종일

    안녕하세요. 해당 기능을 사용하려고 하는데

    라이브 바인딩이 아니라 직접 소스코드로 항목을 추가하려면 어떻게 해야할까요??

구글 통계(Google Analytics)를 써드파티 컴포넌트 없이 구현하는 방법

2016.07.28 10:55

델파이 개발자인 Rafael Ribas Aguiló의 블로그에서 델파이에서 써드파티 컴포넌트 없이 구글 통계(Google Analytics)와 연동하는 내용이 있어 소개합니다.


해당 내용은 안드로이드 기반으로 작성되었습니다. 안드로이드 앱 개발하시는 분들은 참고하시기 바랍니다.


최근에는 앱을 만드는 작업도 중요하지만, 사용성(Usability)을 높이는 작업이 상당히 중요합니다.

여러분들의 고객이 어떤 메뉴나 기능을 얼마나 많이 사용하는지 분석하는 작업에 이용하면 구글 통계를 이용해 더 완성도 높고 사용성 높은 앱을 만들어 보세요.




저작자 표시 비영리 동일 조건 변경 허락
신고

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

[게임] 매의 눈(컵속에 숨겨진 주사위 찾기 게임)

2016.04.27 12:58

구글 플러스 델파이 개발자 그룹에 오대우님이 매의 눈이라는 게임의 소스코드를 공개하셨습니다.

매의 눈은 컵안에 숨겨진 주사위를 찾는 게임으로 현재 안드로이드 마켓에 올려놓으셨네요.


추억의 야바위(?) 게임 다운받아서 술자리에서하면 재밌을 것 같습니다.


소스코드는 드롭박스에 올려주셨네요.(미러도 제공하겠습니다.)




저작자 표시 비영리 동일 조건 변경 허락
신고

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

[OSX/맥] 맥의 스크린세이버 비활성화/활성화 시키기

2016.04.04 13:25

볼랜드포럼에서 무대뽀님이 남겨준 팁 공유합니다.(작업한 내용 공유해주셔서 감사합니다^^)



무대뽀님께서 맥용 애플리케이션 개잘 중 맥의 스크린 세이버를 비활성화 시키는 질문을 볼랜드포럼에 올려서 간단히 댓글을 드렸더니 멋지게 기능을 완성하셨네요.(http://firemonkey.borlandforum.com/impboard/impboard.dll?action=read&db=firemonkey_qna&no=146)


제가 작성한 답변입니다.

맥 개발자 라이브러리에 아래와 같은 샘플이 있습니다. 

https://developer.apple.com/library/mac/samplecode/SceneKitWWDC2014/Listings/Scene_Kit_Session_WWDC_2014_AAPLAppDelegate_m.html 

그 중 아래 코드를 호출하면 됩니다. 
IOPMAssertionID _assertionID; 

- (void)disableDisplaySleeping { 
    CFStringRef reasonForActivity = CFSTR("Scene Kit Presentation"); 
    IOPMAssertionCreateWithName(kIOPMAssertionTypeNoDisplaySleep, kIOPMAssertionLevelOn, reasonForActivity, &_assertionID); 


- (void)enableDisplaySleeping { 
    if (_assertionID) 
        IOPMAssertionRelease(_assertionID); 


위가 잠자는걸 막는 거고 아래는 막은걸 해지하는 겁니다. 

위 메소드 들은 Macapi 중 IOKit에 구현되어 있습니다.(즉 맥의 라이브러리를 이용해야 한다는 거죠.) 
아쉽게도 파이어몽키에 구현된 IOKit을 보면 구현된 API가 몇가지 안됩니다. 
(C:\Program Files (x86)\Embarcadero\Studio\17.0\source\rtl\osx\Macapi.IOKit.pas) 

위 문서와 애플 기술문서를 참고하셔서 필요한 API를 추가하시고 호출하는 코드를 작성해 보시기 바랍니다. 
아래 애플 기술문서가 도움이 될것입니다. 
https://developer.apple.com/library/mac/documentation/IOKit/Reference/IOPMLib_header_reference/#//apple_ref/c/econst/kIOPMAssertionLevelOn 


위 내용을 참고로 Macapi.IOKit.pas에 필요한 메소드를 추가/확장했습니다.

기존의 유닛(C:\Program Files (x86)\Embarcadero\Studio\17.0\source\rtl\osx\Macapi.IOKit.pas)을 확장하기 위해서는 유닛파일(*.pas)를 복사 해 프로젝트 파일(*.dpr)과 같은 경로에 복사후 수정하면 프로젝트 파일과 같은 경로의 유닛을 먼저 참조합니다.


unit Macapi.IOKit 

... 

const 
  kIOMasterPortDefault: mach_port_t = 0; 
  kIOPlatformUUIDKey = 'IOPlatformUUID'; 

  // add 
  kIOPMAssertionLevelOff = 0; 
  kIOPMAssertionLevelOn  = 255; 
  kIOPMAssertionTypeNoDisplaySleep = 'NoDisplaySleepAssertion'; 

... 
type 
... 
function ... 
... 
function IOObjectRelease(anObject: io_object_t): kern_return_t; cdecl; external libIOKit name _PU + 'IOObjectRelease'; 

// add // AssertionID는 Uint32의 포인터형 
function IOPMAssertionCreateWithName(AssertionType : CFStringRef; AssertLevel : uint32_t; 
  AssertionName : CFStringRef; AssertionID : uint32_t) : uint32_t; cdecl; external libIOKit name _PU + 'IOPMAssertionCreateWithName'; 
function IOPMAssertionRelease(AssertionID : uint32_t) : uint32_t; cdecl; external libIOKit name _PU + 'IOPMAssertionRelease'; 


추가한 API를 사용하는 코드는 아래와 같습니다. 버튼을 눌러 비활성화/활성화 시킵니다.

TForm1 = class(TForm) 
  procedure BtnDisableClick(Sender: TObject); 
  procedure BtnEnableClick(Sender: TObject); 
private 
  FSleepAssert : UInt32; 
public 
end; 

implementation 

uses 
  Macapi.IOKit, Macapi.CoreFoundation; 

............ 
procedure TForm1.BtnDisableClick(Sender: TObject); 
begin 
  FSleepAssert := 0; 
  IOPMAssertionCreateWithName(CFSTR(kIOPMAssertionTypeNoDisplaySleep), kIOPMAssertionLevelOn, CFSTR('MyProject is Good'), FSleepAssert); 
  if FSleepAssert = 0 then 
    ShowMessage('Disable Failed!!'); 
end; 

procedure TForm1.BtnEnableClick(Sender: TObject); 
begin 
  if FSleepAssert > 0 then 
    IOPMAssertionRelease(@FSleepAssert); 
end; 

다시한번 좋은 자료 공유해 주신 무대뽀님에게 감사인사를 전합니다.

저작자 표시 비영리 동일 조건 변경 허락
신고

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

  1. Blog Icon
    무대뽀

    제가 Disable에만 신경쓰느라 미쳐 못봐서 실수한부분이 있었네요. 번거롭지만 다시 수정 부탁드려요 ^^;;
    function IOPMAssertionRelease(AssertionID : Pointer) : uint32_t; ... 에서
    파라메터가 Pointer가 아닌 uint32_t 입니다. ㅠ_ㅠ
    function IOPMAssertionRelease(AssertionID : uint32_t) : ... 이고
    예제의 파라메터도 @FSleepAssert가 아닌 @뺀 FSleepAssert로 수정 부탁드릴께요.

    정리된 자료들 아주 잘 보고있습니다. 정말 도움이 많이 되고있습니다.!!
    이자리에서나마 정말 고마움을 표합니다. ^^*

  2. 네. 수정했습니다.
    도움이 되셨다니 제가 감사합니다.
    무대뽀님도 정리차원에서 많은 자료 공유 부탁드립니다.

[FMX] 앱에서 문장을 출력하는 3가지 방법

2016.04.04 11:22

앱에서 긴 글을 표시해야 하는 경우 3가지 방법을 이용해 표현할 수 있습니다.(아래 방법외에도 더 많은 방법이 있을 수 있습니다.)



TextContents.zip


1, TMemo 이용

문장을 나타내는 대표적인 컴포넌트입니다. 문장 출력뿐아니라 입력도 받을 수 있습니다.

출력이 목적인 경우 ReadOnly 속성을 True로 설정하면 입력을 제한합니다.

자동 줄바꿈이 필요하면 TextSettings.WordWrap 속성을 True로 설정합니다.


2, TWebBrowser + HTML 이용

HTML을 표현하는 웹브라우저에 문장을 출력하는 방법입니다. HTML 태그를 이용해 글자크기, 색상, 스타일 등을 자유롭게 사용할 수 있습니다. 만약, 문장이 HTML로 구성된 경우 아주 좋은 선택 입니다.
웹브라우저에 HTML을 불러오려면 아래와 같이 LoadFromString 메소드를 이용합니다.
  WebBrowser1.LoadFromStrings(html, '');

3, TVertScrollBox + TText 이용

수직으로만 스크롤되는 스크롤박스(TVerScrollBox)에 텍스트(TText)를 추가해 문장을 표현하는 방법입니다.

여러개의 텍스트를 추가하면 글자크기, 색상, 스타일을 다르게 지정할 수 있습니다.

텍스트를 상단에 정렬(Align := Top)하면 추가된 순서대로 위에서부터 표시되며, AutoSize 속성을 True로 설정해 높이를 자동 조정할 수 있습니다.

아래 코드는 몇가지 속성을 설정해 동적(코드작성)으로 텍스트를 추가하는 코드 입니다.

procedure TForm1.Button2Click(Sender: TObject);
  procedure AddText(const AText: string; AFontSize: Integer = 12; AFontColor: TAlphaColor = TAlphaColorRec.Black);
  var
    Text: TText;
  begin
    Text := TText.Create(Self);
    Text.Parent := VertScrollBox1;
    Text.Align := TAlignLayout.Top;
    Text.TextSettings.HorzAlign := TTextAlign.Leading;
    Text.TextSettings.Font.Size := AFontSize;
    Text.TextSettings.FontColor := AFontColor;
    Text.AutoSize := True;
    Text.Text := AText;
  end;

var
  S: string;
begin
  AddText('제목1', 20);
  S := 'SVN 서버 소프트웨어는 소스코드 ......';
  S := S + '예를들어, 팀내에서 소스코드를 ......';
  S := S + '만약, 외부와 협업이 필요한 경우 ......';
  AddText(S);

  AddText('제목2', 15, TAlphaColorRec.Red);

  S := 'SVN 서버 소프트웨어는 오픈 소프트웨어 부터 상용 소프트웨어까지 다양하게 선택할 수 있습니다.';
  S := S + '아래 링크를 참고해 원하는 플랫폼과 원하는 SVN 서버 소프트웨어를 설치할 수 있습니다.';
  AddText(S);
end;

추가로, 

델파이 마크다운 라이브러리를 이용할 수 있습니다. 다음 링크를 확인하세요.

http://blog.hjf.pe.kr/428

저작자 표시 비영리 동일 조건 변경 허락
신고

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

[안드로이드/iOS] 이미 배포(Deployment)된 파일 업데이트 하기

2016.03.31 11:06

사운드, 이미지, 텍스트 파일을 최초배포하고, 변경된 파일을 다시 배포할 경우 덮어쓰기(overwrite)가 되지않는 이슈가 있습니다.(10 시애틀. 2016년 3월) 해당 증상은 안드로이드와 iOS 모두 해당됩니다.

해당 이슈는 파일을 배포하는 System.StartUpCopy.pas에 구현된 내용을 살펴보니 배포 대상파일이 있는 경우(FileExist) 배포를 진행하지 않도록 구현되어 있습니다.


위 이슈를 해결하는 방법은 3가지 정도로 생각해볼 수 있습니다.

1, System.StartUpCopy.pas를 수정하는 방법

2, 업데이트할 파일을 새로운 이름으로 배포하고, 앱 시작시 기존 파일로 덮어쓰는 방법

3, 파일을 앱과 함께 최초 배포 후, 파일 변경이 필요할 경우 인터넷 등을 통해 자동 업데이트 하는 방법


1, System.StartUpCopy.pas를 수정하는 방법

http://qc.embarcadero.com/wc/qcmain.aspx?d=125481 링크를 방문해 Xavier Dufaure de Citres 글을 참고해 수정할 수 있습니다.

Xavier Dufaure de Citres at 12/14/2014 3:10:58 PM -
Here is a workaround on Android:
* modify System.StartUpCopy
   - in the interface section add "Var ASSET_OVERWRITE : Boolean = false;"
   - in the interface section exopose "procedure CopyStartUpFiles;"
   - in the CopyAssetToFile change "if (not FileExists(DestFileName)) then" by "if (not FileExists(DestFileName)) or ASSET_OVERWRITE then"

* in your own code, store in the preference file the last version the user ran. If the current version is newer do
  if optLastVersionRan<Version then
  Begin
    ASSET_OVERWRITE := true;
    CopyStartUpFiles;
    ASSET_OVERWRITE := false;
  End;

On the 1st installation the data will be copied twice (no biggy), on update the file will be overwritten.

Note for iOS: i find out that deploying the files i need in ".\Data\" is much better: it put the file directly in the bundle, this way the file are not archived (i got my app rejected because i had 12 Mb of data that was archived). Of course these files can only be read only.


2, 업데이트할 파일을 새로운 이름으로 배포하고, 앱 시작시 기존 파일로 덮어쓰는 방법 

배포파일이 업데이트 되면 새로운 파일명으로 배포 후 앱 시작 시 원래파일로 덮어쓰는 방식입니다.

위 이미지와 같이, "deploy.txt" 파일 배포 시 remote name을 "deploy_2.txt"로 배포했습니다.


프로젝트에는 아래와 같은 코드를 구현했습니다.(코드에 대한 설명은 생략합니다.)

uses
  System.IOUtils;

procedure TForm1.Button1Click(Sender: TObject);
var
  Path: string;
begin
  Path := TPath.Combine(TPath.GetDocumentsPath, 'deploy.txt');
  Memo1.Lines.LoadFromFile(Path);
end;

procedure TForm1.DeployFileUpdate(const AFilename, AUpdateFilename: string);
var
  OrgPath, UptPath: string;
begin
  OrgPath := TPath.Combine(TPath.GetDocumentsPath, AFilename);
  UptPath := TPath.Combine(TPath.GetDocumentsPath, AUpdateFilename);

  if TFile.Exists(UptPath) then
  begin
    if TFile.Exists(OrgPath) then
      TFIle.Delete(OrgPath);
    TFile.Move(UptPath, OrgPath);
  end;
end;

procedure TForm1.FormCreate(Sender: TObject);
begin
  DeployFileUpdate('deploy.txt',  'deploy_2.txt');
//  DeployFileUpdate('sound1.wav',  'sound1_2.wav');
//  DeployFileUpdate('env.ini',     'env_2.ini');
end;


3, 파일을 앱과 함께 최초 배포 후, 파일 변경이 필요할 경우 인터넷 등을 통해 자동 업데이트 하는 방법

배포파일 자동 업데이트는 파일별 버전관리가 필요하며, 업데이트 파일을 내려주는 서버가 필요합니다.

앱 실행 시 배포파일 버전을  서버에서 확인 후 업데이트할 파일이 있으면, Http, FTP 등으로 파일을 다운로드해 배포파일을 교채하는 방식입니다.

웹상의 파일을 다운로드 받는 방법은 아래 링크를 참고할 수 있습니다.

참고링크


  • 모바일(iOS, Android)에서 사용자 파일 배포 및 사용 : http://blog.hjf.pe.kr/104



저작자 표시 비영리 동일 조건 변경 허락
신고

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

델파이 iOS Touch ID 구현하기(래퍼 클래스)

2016.01.28 13:33


Touch ID는 애플 iOS의 LocalAuthentication 프레임워크 통해 구현할 수 있습니다.애플의 Touch ID는 애플의 지문인식 시스템입니다.


델파이에서 Touch ID를 구현하기 위해 LocalAuthentication 프레임워크를 델파이로 래핑한 iOSapi.LocalAuthentication.pas를 다운로드 받아 시스템 PATH에 등록된 경로에 복사합니다.


iOSapi.LocalAuthentication.pas 소스파일 다운로드 및 사용법과 설명은 아래 링크에서 가능합니다.(영문)





저작자 표시 비영리 동일 조건 변경 허락
신고

험프리.김현수 파이어몽키 ios

  1. Blog Icon
    섭이~

    안드로이드쪽 지문인식은 현제 불가능한가요?

[데이터스냅] 데이터스냅 클라이언트에서 TCP/IP로 접속 시 ConnectTimeout이 적용되지 않는 경우 대처방법

2015.11.04 15:55

(이글은 RAD 스튜디오 10 시애틀 기준으로 테스트하고, 작성되었습니다. 다른 버전 사용자들은 이슈 발생여부를 먼저 확인하고 아래 내용을 참고하시기 바랍니다.)


이 글은 데이터스냅 클라이언트에서 TCP/IP로 접속 시 Timeout이 적용되지 않는 이슈에 대해 원인을 확인하고 회피하는 내용을 소개합니다.


데이터스냅 클라이언트 프로그램에서 서버에 접속하기 위해 SQLConnection을 사용합니다.


연결방식(CommunicationProtocol)은 tcp/ip, http, https가 있습니다.

Timeout 속성은 CommnunicationTimeout(송수신 시간제한)과 ConnectTimeout(연결 시간제한) 두가지가 있습니다.


결론적으로 Timeout 속성이 적용되지 않는 경우는 tcp/ip 연결방식으로 연결한 경우 두가지 Timeout 속성이 모두 적용되지 않습니다.(http, https 연결방식에서는 Timeout이 정상 적용됩니다.)

이미 엠바카데로 QC에 이슈가 등록되었지만 적용되지 않는다는 답변으로 아쉽게 마무리되었습니다.

http://qc.embarcadero.com/wc/qcmain.aspx?d=80954#sthash.9Apdxn5i.dpuf


제일  손쉬운 조치방법은 CommunicationProtocol을 http로 변경하는 것입니다.(물론 데이터스냅 서버에서 http를 지원해야 합니다.)


만약, TCP/IP로 연결하실 분들은 아래 내용을 참고하세요.
(RAD 스튜디오 10 시애틀에서 진행했습니다. 다른 버전 사용자분들은 참고해서 수정하시기 바랍니다.)

총 2개의 Unit을 수정하면됩니다. 
아래 2개 파일을 프로젝트 파일 경로로 복사 후 프로젝트에 추가(Add) 합니다.(즉, 해당 프로젝트에서만 수정된 내용이 반영됩니다.)
  • Data.DbxSocketChannelNative.pas(C:\Program Files (x86)\Embarcadero\Studio\17.0\source\data\dbx)
  • IPPeerClient.pas(C:\Program Files (x86)\Embarcadero\Studio\17.0\source\indy\implementation)

Data.DbxSocketChannelNative.pas(C:\Program Files (x86)\Embarcadero\Studio\17.0\source\data\dbx)
procedure TDBXIdTCPLayer.Open(const DBXProperties: TDBXProperties);
var
  timeout: string;
  commTimeout: string;
  LIPVersionStr: string;
  LIPVersion: Integer;
begin
  Close;
  FIPImplementationID := DbxProperties[TDBXPropertyNames.IPImplementationID];
  if FIdSocket = nil then
    FIdSocket := CreateClientSocket;
  FIdSocket.Host := DbxProperties[TDBXPropertyNames.HostName];
  FIdSocket.Port := DbxProperties.GetInteger(TDBXPropertyNames.Port);

  LIPVersionStr := DbxProperties[TDBXPropertyNames.CommunicationIPVersion].Trim;
  LIPVersion := GetEnumValue(TypeInfo(TIPVersionPeer), LIPVersionStr);
  if LIPVersion > -1 then
    FIdSocket.IPVersion := TIPVersionPeer(LIPVersion)
  else
    FIdSocket.IPVersion := TIPVersionPeer.IP_IPv4;

  timeout := DbxProperties[TDBXPropertyNames.ConnectTimeout];
  if timeout = '' then
    ConnectTimeout := 0
  else
    ConnectTimeout := StrToInt(timeout);
  commTimeout := DbxProperties[TDBXPropertyNames.CommunicationTimeout];
  if commTimeout = '' then
    CommunicationTimeout := 0
  else
    CommunicationTimeout := StrToInt(commTimeout);

  { Added Humphrey}
  // ConnectTimeout과 CommunicationTimeout을 FIdSocket에 전달
  if Supports(FIdSocket, IIPPeerClientSetTimeout) then
  begin
    (FIdSocket as IIPPeerClientSetTimeout).SetConnectTimeout(ConnectTimeout);
    (FIdSocket as IIPPeerClientSetTimeout).SetReadTimeout(CommunicationTimeout);
  end;
{ Added Humphrey}

  FIdSocket.UseNagle := false;
  FIdSocket.Connect;

  FConnected := false;
end;
위 메소드는 tcp/ip로 데이터스냅 클라이언트가 접속하는 일부 소스코드입니다.
주의깊게 볼 부분은 ConnectTimeout과 CommunicationTimeout 값을 가져오고 FIdSocket에 전달하는 내용이 누락되었습니다.
그래서 "Added Humphery" 주석으로 감싸진 내용을 추가했습니다.
uses 절에 IPPeerClient 추가해야 합니다.

IPPeerClient.pas(C:\Program Files (x86)\Embarcadero\Studio\17.0\source\indy\implementation)
interface
  { Added Humphrey}
type
  IIPPeerClientSetTimeout = interface
    ['{9924134C-9C7D-464F-8ABE-F3E1E408C566}']
    procedure SetConnectTimeout(const ATimeout: Integer);
    procedure SetReadTimeout(const ATimeout: Integer);
  end;
{ Added Humphrey}
상단 interface 아래에 IIPPeerClientSettimeout 인터페이스 추가

  TIdTCPClientPeerIP = class(TIdClassIP, IIPTCPClient, IIPObject{ Added Humphrey}, IIPPeerClientSetTimeout{ Added Humphrey})
  private
    FTCPClient: TIdTCPClientIP;
    FIOHandler: IIPIOHandler;
    FSocket: IIPIOHandlerSocket;
  protected
    function Connected: Boolean;
    function GetSocket: IIPIOHandlerSocket;
    procedure SetIOHandler(Handler: IIPIOHandler);
    function GetIOHandler: IIPIOHandler;
    function GetBoundIP: string;
    procedure SetBoundIP(IP: string);
    function GetHost: string;
    procedure SetHost(LHost: string);
    function GetPort: TIPPortPeer;
    procedure SetPort(LPort: TIPPortPeer);
    procedure SetIPVersion(const AValue: TIPVersionPeer);
    function GetIPVersion: TIPVersionPeer;
    function GetUseNagle: Boolean;
    procedure SetUseNagle(Use: Boolean);
    procedure Connect;
    procedure Disconnect;
    function GetManagedIOHandler: Boolean;
    procedure SetManagedIOHandler(AManagedIOHandler: Boolean);
  public
    function GetObject: TObject;
    function GetIPImplementationID: string;
    constructor Create(AOwner: TComponent);
    destructor Destroy; override;
{ Added Humphrey}
    procedure SetConnectTimeout(const ATimeout: Integer);
    procedure SetReadTimeout(const ATimeout: Integer);
{ Added Humphrey}
  end;
TIdTCPClientPeerIP 클래스 선언부에 위 IIPPeerClientSetTimeout 인터페이스 위임, 인터페이스 메소드 추가

구현부에 타임아웃 적용하도록 구현
{ Added Humphrey}
procedure TIdTCPClientPeerIP.SetConnectTimeout(const ATimeout: Integer);
begin
  FTCPClient.ConnectTimeout := ATimeout;
end;

procedure TIdTCPClientPeerIP.SetReadTimeout(const ATimeout: Integer);
begin
  FTCPClient.ReadTimeout := ATimeout;
end;
{ Added Humphrey}
위와 같이 조치하면 TSQLConnection으로 DataSnap을 TCP/IP로 연결하는 경우에도 Timeout 속성이 모두 적용됩니다.

완성된 소스코드는 저작권 문제(?)로 올려놓지 못하니 양해부탁드리며 필요하신 분들은 개인적으로 요청 해주세요.


저작자 표시 비영리 동일 조건 변경 허락
신고

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

  1. Blog Icon

    비밀댓글입니다

[따라하기] 건강데이터 수집 및 기록 시스템 #1 - BLE 기반 스마트 체중계에서 실시간 데이터 받기

2015.10.02 13:06


위 동영상(건강데이터 수집 및 기록 시스템 데모) 중 스마트 체중계의 체중정보를 모바일에서 실시간으로 받아오는 기능을 따라하기로 진행합니다.


스마트 체중계에서 체중정보 수신앱 만들기

❑ 앱소개

앱소개

이 앱은 스마트체중계와 블루투스 LE로 연결 해 체중정보를  실시간으로 받아와 화면에 표시하는 앱입니다.

따라하기의 목적

스마트 체중계 연결을 통해 블루투스 LE 인터페이스를 제공하는 다양한 기기(또는 센서)에 연결하고 데이터를 수신하는 방법을 이해합니다.


따라하기에서는 다음 내용을 다룹니다.

  • RAD Studio로 멀티-디바이스(안드로이드, iOS, 윈도우, OS X) 앱 프로젝트 생성 및 화면 디자인
  • TBluetoothLE 컴포넌트로 스마트 체중계와 연결 및 데이터 수신(구독)
  • 실 기기에 배포 및 테스트(안드로이드 폰, 아이폰)

따라하기

0, 사전 준비사항

1, 빈 멀티-디바이스 애플리케이션 프로젝트 생성

2, 메인 UI 제작

3, 블루투스LE 컴포넌트 추가

4, 주변의 디바이스 탐색

5, 탐색된 디바이스 목록에서 디바이스 선택 후 서비스 탐색

6, 탐색된 서비스 목록에서 서비스를 선택 후 특성 구독

7, 특성 데이터 수신 후 체중 데이터 가져오기

8, 블루투스 권한설정 

9, 디바이스에 배포 및 테스트

0, 사전 준비사항

개발도구 준비하기

델파이(또는 RAD Studio)를 이용해 실습 진행합니다.(이 실습은 델파이 10 시애틀 버전으로 작성되었습니다. 델파이 XE8 이상에서 따라할 수 있습니다.) 

만약, 델파이가 설치되지 않았다면 아래의 페이지에서 델파이를 다운로드 받아 설치 후 진행하시기 바랍니다. 

스마트 체중계 준비하기

따라하기에서는 Wahoo Balance Bluetooth Smart Scale 제품을 이용해 실습을 진행합니다.

https://www.underarmour.com/en-us/wahoo-balance-bluetooth-smart-scale/pid1276806-100-OSFA


Wahoo Balance Bluetooth Smart Scale은 다음과 같은 디바이스 이름과 UUID를 갖습니다.

디바이스 이름 : Wahoo Scale v1.3

서비스 UUID : 00001901-0000-1000-8000-00805F9B34FB

특성 UUID : 00002B01-0000-1000-8000-00805F9B34FB

위 정보는 구현에 필요한 정보입니다.(시중의 다른 스마트 체중계는 위 값들이 다릅니다.)

BLE 구조에 대한 소개

블루투스 LE는 프로파일(Profile), 서비스(Service), 특성(Characteristic) 기반으로 아래 그림과 같이 프로파일은 하나 이상의 서비스를 갖고, 서비스는 하나이상의 특성을 갖는 계층구조로 구성되어 있습니다.



(출처: GENERIC ATTRIBUTE PROFILE (GATT) - https://developer.bluetooth.org/TechnologyOverview/Pages/GATT.aspx)


블루투스 LE 인터페이스를 제공하는 장비와 연결하고 데이터를 받기 위해 다음 과정이 필요합니다.(실제 구현도 다음 단계대로 진행합니다.)

1, 주변의 블루투스 LE 프로파일을 탐색합니다.

2, 탐색된 프로파일에서 서비스를 탐색합니다.

3, 탐색된 서비스에서 특성을 찾아 구독(subscribe)합니다.

4, 구독한 특성에서 발생되는 특성값에서 필요한 데이터를 추출합니다.

1, 빈 멀티-디바이스 애플리케이션 프로젝트 생성

  1. File > New > Multi-Device Application - Delphi 메뉴를 선택 하고, Blank Application을 선택해 프로젝트를 생성합니다.
  2. File > Save all 메뉴를 선택 해 유닛이름을 "MainForm.pas"로 프로젝트 이름을 "SmartScale.dproj"로 저장합니다.

2, 메인 UI를 제작

모바일 앱 스타일로 화면 구성하기 위해 폼디자이너 스타일을 Android로 변경합니다.


다음화면과 표를 이용해 체중정보 수신 앱 메인 UI를 개발합니다.


상위 오브젝트 

오브젝트 

속성 

값(또는 설명) 

 Form1

 ToolBar1

 

 

 Layout1

 Align

 Top
 Height 81
 Memo1

 Align

 Client

 ToolBar1

 Label1

 Align

 Client

 Text

 스마트 체중계 데모

 TextSettings.HorzAlign

 Center

 Switch1 Align Right
 Layout1 Text1

 Align

 Center

 Text

 0.0
 TextSettings.Font.Size

 30

 TextSettings.HorzAlign Trailing
 Width 114
 Text1 Text2 Position.X 120
 Text Kg

 TextSettings.HorzAlign

 Leading

3, 블루투스 LE 컴포넌트 추가

블루투스 LE 컴포넌트 추가

Tool Palette에서 TBluetoothLE 컴포넌트 선택 해 폼에 추가합니다.


필요한 변수, 메소드, 상수 추가

구현에 필요한 변수(디바이스, 서비스, 특성), 메소드 선언(시작, 중지), 상수(장비명과 서비스, 특성 UUID)를 아래와 같이 추가합니다.

  private
    { Private declarations }
    FBLEDevice: TBluetoothLEDevice;
    FBLEGattService: TBluetoothGattService;
    FBLEGattChar: TBluetoothGattCharacteristic;

    procedure StartScale; // 체중계 연결 동작 시작(블루투스LE 활성화, 주변 디바이스 탐색)
    procedure StopScale; // 체중계 연결 동작 중지(블루투스LE 비활성화, 특성 구독 해제)
  public
    { Public declarations }
  end;

const
  ScaleDeviceName = 'Wahoo';

  WEIGHT_SERVICE: TBluetoothUUID          = '{00001901-0000-1000-8000-00805F9B34FB}';
  WEIGHT_CHARACTERISTIC: TBluetoothUUID   = '{00002B01-0000-1000-8000-00805F9B34FB}';

메소드 구현부 생성

StartScale, StopScale 메소드 구현부를 자동 생성하기 위해 procedure StartScale;에 마우스 커서를 옮기고 키보드에서 Ctrl + Shift + C를 동시에 누릅니다.


4, 주변의 디바이스 탐색

주변 디바이스 탐색

Switch1의 OnSwitch 이벤트 핸드러 생성(Switch1 컴포넌트 선택 > Object Inspector 창에서 Event 탭 선택 > OnSwitch 우측의 콤보박스 더블클릭) 후 아래 코드를 입력합니다.

procedure TForm1.Switch1Switch(Sender: TObject);
begin
  if Switch1.IsChecked then
    StartScale
  else
    StopScale;
end;

StartScale 메소드에서 블루투스 LE를 활성화하고, 주변 디바이스를 탐색하는 코드를 다음과 같이 입력합니다.
(StopScale은 제일 마지막에 다시 구현합니다.)

procedure TForm1.StartScale;
begin
  BluetoothLE1.Enabled := True;
  BluetoothLE1.DiscoverDevices(1000); // 1초(1000 ms)동안 주변 디바이스 탐색, 완료 시 OnEndDiscoveryDevices 이벤트 발생
end;

5, 탐색된 디바이스 목록에서 디바이스 선택 후 서비스 탐색

StartScale 메소드의 DiscoverDevices 메소드 호출하면 (주변의 디바이스 탐색 후) OnEndDiscoverDevices 이벤트가 발생합니다. 

OnEndDiscoverDevices 이벤트는 파라메터로 탐색된 디바이스 목록(ADeviceList)을 제공합니다.

이 디바이스 목록에서 'Wahoo'(위에서 ScaleDeviceName 상수로 선언)로 시작하는 디바이스를 선택합니다.

디바이스를 선택했다면, 디바이스에서 제공하는 서비스 탐색을 시도합니다.

procedure TForm1.BluetoothLE1EndDiscoverDevices(const Sender: TObject;
  const ADeviceList: TBluetoothLEDeviceList);
var
  Device: TBluetoothLEDevice;
begin
  if ADeviceList.Count = 0 then
  begin
    Memo1.Lines.Add('발견된 디바이스가 없습니다.');
    Switch1.IsChecked := False; // 디바이스 미발견 시 스위치 끄기
    Exit;
  end;

  FBLEDevice := nil;
  Memo1.Lines.Add('Device List');
  for Device in ADeviceList do
  begin
    Memo1.Lines.Add(' - ' + Device.DeviceName); // 로그-디바이스 이름

    // 디바이스 이름으로 디바이스 선택
    if Device.DeviceName.StartsWith(ScaleDeviceName) then
    begin
      FBLEDevice := Device;
      Memo1.Lines.Add('디바이스를 찾았습니다.');
    end;
  end;

  if FBLEDevice = nil then
    Exit;

  // [TIP] 서비스 발견이 실패하는 경우가 종종 있음
    // 실패한 경우 한번더 발견요청
  if not FBLEDevice.DiscoverServices then
    FBLEDevice.DiscoverServices; // 서비스 탐색 완료 시 OnEndDiscoverServices 이벤트 발생
end;

6, 탐색된 서비스 목록에서 서비스를 선택 후 특성 구독

서비스 탐색(DiscoverServices)이 완료되면 OnEndDiscoverServices 이벤트가 발생합니다.

OnEndDiscoverServices 이벤트는 파라메터로 탐색된 서비스 목록(AServiceList)을 제공합니다.

이 서비스 목록에서 서비스 UUID와 같은 UUID를 갖는 서비스를 선택합니다.

선택한 서비스에서 특성 UUID로 특성을 찾습니다.

특성에서 체중정보를 포함한 특성데이터를 수신할 수 있도록 구독(SubscribeToCharacteristic) 합니다.

procedure TForm1.BluetoothLE1EndDiscoverServices(const Sender: TObject;
  const AServiceList: TBluetoothGattServiceList);
var
  Service: TBluetoothGattService;
begin
  Memo1.Lines.Add('Service List');
  for Service in AServiceList do
  begin
    Memo1.Lines.Add(' - ' + Service.UUID.ToString); // 로그-디바이스 이름
    if Service.UUID = WEIGHT_SERVICE then
    begin
      FBLEGattService := Service;
      Memo1.Lines.Add('서비스를 찾았습니다.');
    end;
  end;

  if FBLEGattService = nil then
  begin
    Memo1.Lines.Add('서비스를 찾지 못했습니다.');
    Exit;
  end;

  // [TIP][예외] 특성목록을 생성하기 위해 아래 코드 호출
    // GetCharacteristic 바로 호출 시 특성을 가져오지 못해
    // GetCharacteristics을 먼저 호출
  BluetoothLE1.GetCharacteristics(FBLEGattService);

  FBLEGattChar := BluetoothLE1.GetCharacteristic(FBLEGattService, WEIGHT_CHARACTERISTIC);
  if FBLEGattChar = nil then
  begin
    Memo1.Lines.Add('서비스 특성을 찾지 못했습니다.');
    Exit;
  end;

  if BluetoothLE1.SubscribeToCharacteristic(FBLEDevice, FBLEGattChar) then // 구독하면 OnCharacteristicRead 이벤트를 통해 특성 값을 받음
    Memo1.Lines.Add('특성에 구독했습니다.'); 
end;

7, 특성 데이터 수신 후 체중 데이터 가져오기

특성에 구독(SubscribeToCharacteristic)하면 TBluetooth 컴포넌트의 OnCharacteristicRead 이벤트로 특성 값을 받을 수 있습니다. 스마트 체중계의 경우 체중계의 체중이 변경될 때마다 특성 값을 받을 수 있습니다.

Wahoo Balance Blueeoth Smart Scale은 다음과 같은 코드로 특성값에서 체중 정보를 가져옵니다.

(특성 값을 분석하는 방법은 서비스마다 다릅니다.)

procedure TForm1.BluetoothLE1CharacteristicRead(const Sender: TObject;
  const ACharacteristic: TBluetoothGattCharacteristic;
  AGattStatus: TBluetoothGattStatus);
var
  Weight: Single;
begin
  Weight := (ACharacteristic.GetValueAsInteger shr 8) / 10;
    // 특성 값에서 앞의 3byte 이용, 마지막 숫자는 소숫점이므로 10으로 나누기

  Text1.Text := Format('%3.1f', [Weight]);
end;

마지막으로 앞에서 선언한 StopScale에 특성 구독을 해지하고, 블루투스 LE를 비활성화하는 코드를 추가합니다.

procedure TForm1.StopScale;
begin
  if Assigned(FBLEDevice) and Assigned(FBLEGattChar) then
    BluetoothLE1.UnSubscribeToCharacteristic(FBLEDevice, FBLEGattChar);
  BluetoothLE1.Enabled := False;
end;

8, 블루투스 권한설정

(이 과정은 안드로이드 플랫폼을 사용하지 않는 경우 생략할 수 있습니다.)

비콘은 블루투스 LE(Low Energy) 기반으로 통신합니다.  블루투스 사용권한을 설정합니다.

Project > Options 메뉴를 선택 후 Uses Permissions 화면으로 이동합니다.


Bluetooth와 Bluetooth admin 두개의 권한을 사용하도록 설정합니다.


9, 디바이스에 배포 및 테스트

처음 디바이스에 배포하는 경우 먼저 모바일 개발환경 설정하고 진행하기 바랍니다.


개발을 완료했습니다. 이제 준비된 개발환경으로 플랫폼 선택 후 Run > Run Without Debugging 메뉴를 이용해 실행하고 테스트 합니다.



테스트 방법:

  1. 디바이스의 블루투스 기능을 킵니다.
  2. 앱을 실행 후 스위치를 커서 주변의 블루투스 LE를 탐색해 스마트 체중계와 연결합니다.
    (메모 컨트롤에 "디바이스를 찾았습니다." > "서비스를 찾았습니다." > "특성에 구독했습니다." 메시지가 표시되면 정상 연결된 것입니다.)
  3. 체중계에 올라가면 실시간으로 앱에 체중정보가 표시되는 것을 확인합니다.

저작자 표시 비영리 동일 조건 변경 허락
신고

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

  1. Blog Icon
    이승욱

    안녕하세요.
    지난 9월에 세미나를 교수님과 다녀온 후에 학과에서 간단하게 시연해보려고 계속 작업중에 있습니다. 비콘을 이용한 경보 앱은 구현을 하고 이번에는 체중계 앱을 작업하고 있는데 문제가 있어 조언을 얻고자 글을 남기게 되었습니다.
    기존에 포스팅 하신데로 모두 설정하였고 UUID도 맞게 입력이 되었는데 컴파일 하고 연결하려고 하면 디바이스가 1개 검색이 되지만 weight_service를 불러오지 못한다고 합니다.
    현재 작업하는 디바이스는 탭이고 개별로 블루투스로 연결해볼까 해서 연결하면 Wahoo Scale v1.3 에서 연결을 거절했다는 메시지가 뜨네요...
    답변해주시면 감사하겠습니다.

  2. 디바이스 발견 시 본문의 화면과 같이 Wahoo Scale v1.3으로 표시되나요?
    쳬중계를 연결할때 미리 체중계에 올라가 체중계를 키고 시도해 보세요.

    그리고, 혹시 모르니 다른 안드로이드 기기로도 테스트해보시기 바랍니다.
    (그럴리 없지만 체중계의 이상을 확인하려면 아이폰에서 Wahoo Wellness 앱을 설치해 연결을 확인해보세요. 무료입니다.)

    그래도 안되면 다음 링크에 데모 샘플코드가 있으니 해당 소스코드로 테스트해보시기 바랍니다.
    https://github.com/devgear/RADStudio_IoTDemos

    마지막으로 시간되시면 제가 사물인터넷 1일 과정 교육을 진행하고 있습니다.
    해당 교육에서 스마트 체중계 연결 실습도 진행되니 오프라인으로 확인하고 싶으시면 교육신청해주세요.^^
    http://devgear.co.kr/support/education/radstudio_iot.html

  3. Blog Icon
    김헌성

    안녕하세요~

    블루투스 저울로 스마트폰 앱을 만드는것을 공부하고 있습니다.

    혹시 체중계별로 UUID가 다르다고 하셨는데요..

    Wahoo Balance Bluetooth Smart Scale 이제품의 UUID는 어떻게 아신건가요??

    공개를 해주나요??아니면 다른방법으로 아는 방법이 있나요??

    저는 블루투스 정밀저울로 테스트를 해야해서요..ㅠ

    제가 테스트하려는 제품은 Smart Connect Bluetooth Kitchen Scale 입니다.

    답변주시면 감사하겠습니다. ㅎ

  4. Blog Icon
    박상우

    안녕하십니까. 비콘과 앱 개발을 처음 해보는 학생입니다.
    비콘에 대한 정확한 개념도 없이 막 시작하는 터라 궁금한 점이 한두가지가 아닌데, 초면에 무례함을 무릎쓰고 몇가지 질문 드려도 될까요?

    우선 첫 번째로 비콘을 이용하여 자동 출석체크 시스템을 만드려는데, 강의장이라고 인식되는 범위 내에 단말이 들어오면 단말에 앱 알람이 뜨며 해당 강의실의 출석 버튼이 오픈되고, 출석 버튼을 누르면 출석이 인정되게 하는 앱을 구현하고 싶습니다. 가능할지 궁금합니다.

    첫번째단계까지 구현하고 난 다음에는 출석 후 단말이 30분 이상 해당 지역에 없으면 출석인정이 안되도록 하고 싶은데, 비콘에 따라 로그를 남겨 그 기록을 바탕으로 빅데이터 처리를 할 수 있다고 해서 이 기능을 이용하고 싶은데 가능할지 궁금합니다.

    세 번째로는 비콘의 종류는 상관이 없는것인지, 비콘에 따라 구현이 다르게 되는지에 대한 궁금증입니다.

    바쁘신 와중에 질문드려 죄송합니다. 그리고 읽어주셔서 감사합니다.

  5. 1, 원하는 기능 구현 가능합니다. 미들웨어 형태의 서버가 필요하겠네요. 모바일 앱에서 비콘 신호를 받아 출석버튼을 활성화 하고 버튼 누르면 서버를 통해 출석처리하도록 요청하도록 하세요.

    2, 모바일 앱을 계속 켜놓고, (예로)1분마다 서버로 비콘과의 거리등을 전송하면 서버에서 30분이상 해당 지역에 있었는지 판단할 수 있습니다.

    3, 비콘이 아이비콘, 알트비콘 주종의 표준을 따랐다면 비콘의 종류와 관계없이 대부분의 비콘을 연동할 수 있습니다.

    비콘 연동하는 내용은 http://blog.hjf.pe.kr/384 을 참고하세요.

  6. Blog Icon
    박상우

    빠른 답변 대단히 감사드립니다. 혹시 eclipse에서도 구현할 수 있는 기능인가요? 제가 pascal어를 공부해본 적도, rad studio를 사용해본 적도 없어서 잘 할 수 있을지가 걱정이 됩니다..답변 다시한번 대단히 감사합니다. 큰 도움이 되었습니다.

  7. 개념과 구성만 잘 잡고 개발하신다면 툴(언어)가 문제가 되진 않을 것입니다.
    자바에서도 비슷한 기능을 제공하니까 살펴보시기 바랍니다.

    기회되면 델파이도 한번 관심 갖어주세요^^
    (델파이 교육과정 : http://devgear.co.kr/edu
    대학생분들은 5명까지 무료로 참석할 수 있습니다.)

  8. Blog Icon

    비밀댓글입니다

  9. 제가 테스트한 시점에 인바디 정보를 다루지 않아 정확한 내용을 모르겠습니다.

    만약, 체중계가 인바디 정보를 제공한다면,
    충분히 가져올 수 있습니다.

    위 샘플에서는 원하는 체중정보를
    프로필 > 서비스 > 특성 > 속성 > 값으로 원하는 값을 찾아서 사용한 것이기 때문에
    위 매커니즘으로 인바디 정보를 제공한다면 가져올 수 있을 것으로 보입니다.

  10. Blog Icon
    이원석

    감사합니다. 공부하는데 많은 도움이 되었습니다.
    다름이 아니오라, 여러개의 블루투스에서 동시에 값을 받아올수 있을까요?
    이를테면 체중계를 여러개 연결하여, 1번 체중계, 2번 체중계 데이터를 동시에 받는
    형태입니다.
    염치없이 질문 드려봅니다.
    감사합니다.

  11. 컴포넌트 구조 상 컴포넌트 여러개를 두고 연결하면 가능할 것으로 생각합니다.

    하지만, 직접해보시고 가능여부를 판단하는 것이 좋습니다.

iOS9의 새로운 기능인 "App transport Security" 예외 허용을 위한 Info.plist xml 수정방법

2015.10.01 13:11

이 글은 엠바카데로의 David I 블로그 글을 인용 및 참고해 작성한 글입니다. 자세한 내용은 원글을 통해서 확인하시기 바랍니다.

http://community.embarcadero.com/blogs/entry/how-to-use-custom-info-plist-xml-to-support-ios-9-s-new-app-transport-security-feature

iOS9의 App Transport Security 예외 허용하기

iOS9에서 애플은 내부적으로 HTTP 프로토콜 요청하는 응용프로그램을 제한하는 새로운 "App Transport Security" 기능을 추가했습니다. 


App Transport Security

“App Transport Security is a feature that improves the security of connections between an app and web services. The feature consists of default connection requirements that conform to best practices for secure connections. Apps can override this default behavior and turn off transport security. Transport security is available in iOS 9.0 or later, and in OS X v10.11 and later.”


간단하게 요약하면 iOS9 부터는 HTTP(암호화 되지 않은 통신)에 대해서 OS 단에서 제한을 한다는 내용입니다.


애플에서는 Info.plist를 설정해 앱에서 HTTP를 사용할 수 있도록 허용합니다. 

이 글에서는 RAD Studio에서 Info.plist 옵션을 설정하는 방법과 RAD Studio 10 시애틀 샘플소스코드 프로젝트에 대한 링크를 제공합니다.


❑ Info.plist 편집 방법

Info.plist 파일은 프로젝트를 빌드 할때마다 다시 생성됩니다. 그렇기 때문에 Info.plist 파일을 직접 편집하기 위해서는 예를들어 프로젝트의 디렉토리와 같은 위치에 Info.plist 파일을 복사 후 복사한 파일을 편집해야 합니다.

(Info.plist 파일은 iOS Device - 32 bit / 64 bit로 타겟 선택 후 프로젝트를 빌드하면 프로젝트 저장 경로의 하위에 생성됩니다.

예> \iOSDevice32\Debug\Project1.Info.plist)


복사 후 편집한 Info.plist 파일은 Project > Deployement 기능을 통해 별도로 배포해야합니다.

(이 과정은 이 글의 후반부에 다시 안내합니다.)


❑ App Transport Security 옵션 설정

옵션1. "App Transport Security" 비활성화


<key>NSAppTransportSecurity</key> <dict> <key>NSAllowsArbitraryLoads</key><true/> </dict>


Info.plist 파일의 제일 마지막으로 이동 후 </dict> 태그 바로 위에 위의 코드를 추가합니다.

이 방법은 TWebBrowser, Indy HTTP 등의 컴포넌트를 사용하는 경우 앱에서 사용하는 모든 HTTP 통신에 대해 App Transport Security 기능을 해제하는 설정입니다.


옵션2. 'App Transport Security" 예외 도메인 등록


<key>NSAppTransportSecurity</key> <dict> <key>NSExceptionDomains</key> <dict> <key>appanalytics.embarcadero.com</key> <dict> <key>NSExceptionAllowsInsecureHTTPLoads</key><true/> </dict> </dict> </dict> 

Info.plist 파일의 제일 마지막으로 이동 후 </dict> 태그 바로 위에 위의 코드를 추가합니다.


이 방법은 특정 도메인(appanalytics.embarcadero.com)으로 접속하는 연결에 대해 보안이 적용되지 않은 HTTP 통신을 허용하도록 설정하는 방법입니다.



❑ Info.plist 배포 설정

앞에서 수정한 Info.plist 파일을 기본 Info.plist를 대신하도록 배포설정해야 합니다. 이 과정은 iOS Device 32 bit와 64 bit 모두에서 진행해야 합니다.(32 비트와 64비트 버전이 약간 다르다는 것을 주의)

배포 관리자(Project > Deployement)로 이동 후 편집한 Info.plist 파일을 추가(Add file)합니다.


아래 그림과 같이 기본 Info.plist 파일을 선택 해제합니다.(배포 대상에서 제외)

AppAnalytics_Info.plist_Project_Deployment

위와같이 배포 설정후 앱을 배포하면 변경된 설정의 Info.plist가 배포됩니다.

관련 링크



저작자 표시 비영리 동일 조건 변경 허락
신고

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

[따라하기] 위험지역 경보 시스템 #1 - 비콘을 이용해 위험지역 진입 경보앱 만들기

2015.07.28 18:03


위 동영상(위험 지역 경보 시스템 데모) 중 위험 지역 진입 시 경보를 발생하는 앱을 따라하기를 통해 만들어 봅니다.

위험지역 진입 경보앱 만들기

❑ 앱 소개

앱 소개

이 앱은 위험지역에 진입 시 화면과 경고음으로 위험지역에 진입을 경고해 주는 (프로토 타입)앱입니다.


따라하기의 목적

따라하기를 통해 비콘을 이용한 위치기반 앱 개발을 시작합니다.


이 따라하기에서는 다음 내용을 다룹니다.

  • 비콘 컴포넌트를 이용해 비콘과의 거리를 활용합니다.
  • UI 컨트롤의 투명도에 애니메이션 효과를 적용해 화면경고 기능을 구현합니다.
  • 사운드 파일(*.mp3)을 앱과 배포해 경보음으로 사용합니다.

따라하기

0, 사전 준비사항

1, 빈 멀티-디바이스 애플리케이션 프로젝트를 생성하고 저장합니다.

2, 메인 UI를 만듭니다.

3, 위험 경보 UI를 추가합니다.

4, 위험경고 화면 효과, 경고음 기능을 추가합니다.

5, 비콘 컴포넌트 추가 후 이벤트를 만듭니다.

6, 비콘과 거리를 기반으로 위험지역 경보 이벤트를 발생합니다.

7, 블루투스 권한설정

8, 디바이스에 배포하고 테스트합니다.

0, 사전 준비사항

개발 도구 준비하기

델파이(또는 RAD Studio)를 실행합니다.(이 실습은 델파이 XE8 버전으로 작성되었습니다.)

만약, 델파이가 설치되지 않았다면 아래의 페이지에서 델파이를 다운로드 받아 설치 후 진행하시기 바랍니다.

따라하기에서 사용할 리소스 다운로드

DangerZoneAlertRes.zip


위 파일을 다운로드 후 적당한 위치에 압축해제하시기 바랍니다.

비콘 준비하기

비콘을 준비합니다.(시중에서 다양한 비콘을 판매하고 있습니다. 저는 Beacon B1을 이용해 진행했습니다.)
시중에서 판매되는 (표준을 지키는)대부분의 비콘과 연동됩니다.

비콘 정보 확인/설정하기

비콘의 UUID와 Major Id, Minor Id를 알아야 합니다. 제조사 사이트 또는 비콘 스캐너 앱을 통해 파악할 수 있습니다.
보통 같은 제품의 비콘은 동일한 UUID, Major Id, Minor Id로 설정되어있습니다. 비콘 별로 구별하기 위해 Major Id, Minor Id를 변경해야 하는데, 제조사의 안내에 따라 Major Id, Minor Id를 다르게 설정하기 바랍니다.(대부분 제조사에서 전용 앱을 제공합니다.)

1, 빈 멀티-디바이스 애플리케이션 프로젝트 생성

  1. File > New > Multi-Device Application - Delphi 메뉴를 선택 하고, Blank Application을 선택해 프로젝트를 생성합니다.
  2. File > Save all 메뉴를 선택 해 유닛이름을 "MainForm.pas"로 프로젝트 이름을 "DangerZoneAlert"으로 저장합니다.

2, 메인 UI를 만듭니다.

모바일 앱 스타일로 화면 구성하기 위해 폼디자이너 스타일을 Android로 변경합니다.


다음화면과 표를 이용해 위험지역 경보 앱 메인 UI를 개발합니다.
(이 따라하기는 비콘 활용 기능에 집중하기 위해 최소한의 UI로 구성했습니다.)


상위 오브젝트

오브젝트

속성

값(또는 설명)

 Form1

 ToolBar1

 

 

 ToolBar1

 Label1

 Align

 Client

 Text

 위험지역 경보 앱

 TextSettings.HorzAlign

 Center

 Switch1

 Align

 Right

 Form1

 Image1

 MultiResBitmap

 "factory.png 선택"

 StatusBar1

 Height

 32

 StatusBar1

 Label2

 Align

 Client

 Text

 위험지역과의 거리: 

3, 위험 경고 UI를 추가합니다.

위험 지역 진입 시 화면으로 경고를 주기위한 UI를 추가합니다. 붉은색 배경에 안내문구를 표시하고, 화면을 깜빡이게 합니다.

화면을 깜빡이는 기능은 다음 단계에서 진행합니다.


다음화면과 표를 이용해 위험 경보 UI를 추가합니다.


상위 오브젝트 

 오브젝트

 속성

 값(또는 설명)

 Form1

 Rectangle1

 Align

 Client

 Fill.Color

 Red

 Opacity

 0.8

 Rectangle1

 Layout1

 Align

 Center

 Width/Height

 228/186(적당히)

 Layout1

 Label3

 Align Text
 Text

 위 그림 참고(위험지역에 ...)

 lblDistance Align Bottom
 Name lblDistance

 TextSettings.Font.Size

 24
 TextSettings.HorzAlign Center

4, 위험경고 화면 효과, 경고음 기능을 추가합니다.

위험지역 진입을 알리기위해 경고음을 발생하고, 경고화면을 표시 후 화면을 깝빡여주는 기능을 구현합니다.


경고음을 실행하기위해 TMediaPlayer 컴포넌트를 경고화면을 깜빡이기 위해 TFloatAnimation 컴포넌트를 추가합니다.


Rectangle1의 FloatAnimation은 Opacity 속성 추가 메뉴(Create New TFloatAnimation)를 선택해 생성합니다.


상위 오브젝트

오브젝트

속성

값(또는 설명)

 Form1

 MediaPlayer1

 

 

 Rectangle1

 FloatAnimation1

 Autoreverse

 True

 Duration

 1

 Loop

 True

 PropertyName Opacity

 StartValue/StopValue

 0.8/0.2

경고음 사운드 파일 배포등록하기

구현에 앞서 경고음으로 사용할 사운드파일을 배포관리자에 등록힙니다.

  1. 메인메뉴에서 Project > Deployment 메뉴를 선택해 Deployment 윈도우를 표시합니다.
  2. Add files 버튼을 누르고 "alert.mp3"파일을 선택해 추가합니다.
  3. 배포 플랫폼 선택 콤보박스를 Android platform과 iOS Device platform으로 선택하고, 아래를 참고해 Remote Path를 수정합니다.

    • Android platform - assets\internal\
    • iOS Device platform - StartUp\Documents\

경고 기능 구현하기

private 영역에 아래 코드를 참고해 4개의 메소드를 선언합니다.

  private
    { Private declarations }
    procedure StartWarning; // 위험지역 진입 경고 시작
    procedure StopWarning;  // 위험지역 진입 경고 중지

    procedure StartSiren;   // 사이렌 시작
    procedure StopSiren;    // 사이렌 중지
  public

Ctrl + Shift + C 단축키(Complete Class at Cursor)를 이용해 메소드의 구현부를 자동완성합니다.

아래 코드를 참고해 구현부를 완성합니다.

procedure TForm1.StartSiren;
begin
  MediaPlayer1.FileName := TPath.Combine(TPath.GetDocumentsPath, 'alert.mp3');
  MediaPlayer1.Play;
end;

procedure TForm1.StopSiren;
begin
  MediaPlayer1.Stop;
end;

procedure TForm1.StartWarning;
begin
  Rectangle1.Visible := True;
  FloatAnimation1.Enabled := True;

  StartSiren;
end;

procedure TForm1.StopWarning;
begin
  Rectangle1.Visible := False;
  FloatAnimation1.Enabled := False;

  StopSiren;
end;

사운드파일 경로 설정을 위해 사용한 TPath가 선언된 System.IOUtils 유닛을 유즈절에 추가합니다.

5, 비콘 컴포넌트 추가 후 이벤트를 만듭니다.

비콘 컴포넌트(TBeacon)는 안드로이드, iOS, OSX를 지원합니다. 타겟 플랫폼을 안드로이드로 변경 후 폼위에 비콘 컴포넌트를 추가합니다.


모니터링 대상 비콘 등록

비콘을 모니터링하기 위해 비콘 정보를 등록합니다.

  1. Beacon1 컴포넌트의 MonitorizedRegions 속성을 선택 후 확장 버튼을 클릭합니다.

  2. 편집창에서 Add new 버튼을 눌러 항목을 추가합니다.

  3. 대상 비콘의 UUID, Major, Minor 값을 입력합니다.

(만약, Major, Minor 값을 -1로 설정하면 UUID만 이용해 대상을 탐색합니다.)


비콘 인스턴스 할당하기

비콘 변수를 선언하고, BeaconEnter, BeaconExit 이벤트에서 비콘 인스턴스를 할당해줍니다.


private 영역에 변수를 선언합니다.

  private
    { Private declarations }
    FBeacon: IBeacon;

 비콘 컴포넌트의 OnBeaconEnter, OnBeaconExit 이벤트 핸들러를 만들고 비콘 인스턴스를 할당합니다.

(이벤트 핸들러는 이벤트의 우측 영역을 더블클릭하면 생성됩니다.)


procedure TForm1.Beacon1BeaconEnter(const Sender: TObject;
  const ABeacon: IBeacon; const CurrentBeaconList: TBeaconList);
begin
  FBeacon := ABeacon;
end;

procedure TForm1.Beacon1BeaconExit(const Sender: TObject;
  const ABeacon: IBeacon; const CurrentBeaconList: TBeaconList);
begin
  FBeacon := nil;
end;

비콘 컴포넌트 동작을 활성화하기

스위치(Switch1)를 키면 비콘이 동작하도록 Switch1의 OnSwitch 이벤트 핸들러 생성 후 아래 코드를 입력합니다.

procedure TForm1.Switch1Switch(Sender: TObject);
begin
  Beacon1.Enabled := Switch1.IsChecked;
end;

6, 비콘과 거리를 기반으로 위험지역 경보 이벤트를 발생합니다.

이제 모든 준비는 완료되었고, 비콘과의 거리를 계산해 위험지역 진입 경보 이벤트를 발생하면 됩니다.

위험지역 진입에 대한 경보는 아래와 같은 기준으로 시작하고 중단합니다.

위험지역 진입 경보 기준

  1. 비콘과의 거리가 1.0미터 이하인 경우 위험지역으로 간주한다.
  2. 경보가 아닌 상태에서 위험지역에 3초이상 연속으로 머무른 경우 경보를 시작한다.
  3. 경보가 발생중인 상태에서 위험지역을 3초이상 연속으로 벗어난 경우 경보를 중단한다.

비콘과 거리를 주기적으로 확인하기 위해 타이머 컴포넌트(Timer1)를 폼에 추가합니다.


아래 변수를 private 영역에 선언합니다.

    FIsWarning: Boolean;          // 경고 중인가?
    FDangerAreaStaySecs,          // 위험지역에 머무른 시간(초)
    FDangerAreaOutSecs: Integer;  // 위험지역에서 벗어난 시간(초)


Timer1의 OnTimer 이벤트 핸드러를 생성하고, 아래 코드를 추가합니다.

procedure TForm1.Timer1Timer(Sender: TObject);
var
  InDangerArea: Boolean;  // 위험지역에 있는가?(비콘과의 거리가 1m 이내인가?)
begin
  if Assigned(FBeacon) then
  begin
    InDangerArea := FBeacon.Distance <= 1;

    // 경고 중 아님
      // 위험지역에 3초간 머무른 경우 경고 시작
    if not FIsWarning then
    begin
      if InDangerArea then
        Inc(FDangerAreaStaySecs)
      else
        FDangerAreaStaySecs := 0
      ;

      if FDangerAreaStaySecs >= 3 then
      begin
        FIsWarning := True;
        StartWarning;
        FDangerAreaStaySecs := 0
      end;
    end
    // 경고 중
      // 위험지역을 3초 이상 벗어난 경우 경고 중단
    else if FIsWarning then
    begin
      if not InDangerArea then
        Inc(FDangerAreaOutSecs)
      else
        FDangerAreaOutSecs := 0;

      if FDangerAreaOutSecs >= 3 then
      begin
        FIsWarning := False;
        StopWarning;
        FDangerAreaOutSecs := 0;
       end;
    end;

    // 위험지역과의 거리를 표시
    lblDistance.Text := FBeacon.Distance.ToString;
    Label3.Text := '위험지역과의 거리: ' + FBeacon.Distance.ToString + ' m';
  end;
end;

7, 블루투스 권한설정

(이 과정은 안드로이드 플랫폼을 사용하지 않는 경우 생략할 수 있습니다.)

비콘은 블루투스 LE(Low Energy) 기반으로 통신합니다.  블루투스 사용권한을 설정합니다.

Project > Options 메뉴를 선택 후 Uses Permissions 화면으로 이동합니다.


Bluetooth와 Bluetooth admin 두개의 권한을 사용하도록 설정합니다.

8, 디바이스에 배포하고 테스트합니다.

처음 디바이스에 배포하는 경우 먼저 모바일 개발환경 설정하고 진행하기 바랍니다.


개발을 완료했습니다. 이제 준비된 개발환경으로 플랫폼을 선택 후 Run > Run Without Debugging 메뉴를 이용해 실행하고 테스트 합니다.



테스트 방법 : 

  • 앱을 실행 후 비콘과 1미터 이내로 접근 후 3초이상 머무르면 경고가 발생
  • 경고 발생 중 1미터 초과 거리로 이동 후 3초이상 머무르면 경고 중단


참고자료



저작자 표시 비영리 동일 조건 변경 허락
신고

험프리.김현수 파이어몽키 Beacon, IOT, 비콘, 사물인터넷

  1. Blog Icon

    비밀댓글입니다

  2. 이번 따라하기에는 로그로 기록하고, 관리자에게 보고하는 내용이 포함되지 않았습니다.

    영상에서 사용된 앱에서는 주기적으로 클라우드에 위험지역과의 거리를 기록하고, 위험지역에 머무른 경우 관리자에게 푸쉬메시지(GCM)을 전송했습니다.

    이 내용에 관심이 있으시다면 제가 진행하는 세미나에서 자세한 이야기를 들어보세요.(8월 11일은 이미 신청완료 됐고, 9월 3일에 앵콜세미나가 진행됩니다.)
    http://tech.devgear.co.kr/board_in1/413343

  3. Blog Icon

    비밀댓글입니다

  4. 비콘은 XE8에서 추가된 컴포넌트입니다.

    업데이트 또는 트라이얼 버전으로 진행해주세요^^

  5. Blog Icon
    김태규

    그럼 따로 비콘 업체에서 제공해주는 api 가져다가 만들어야하나요?

    비콘 컴포넌트 없이도 제작은 가능하죠??

  6. 네 비콘 표준을 기반으로 직접 구현할 수 있습니다.(iBeacon, AltBeacon을 필요한 플랫폼 별로 구현해야 합니다.) 하지만 쉽지 않습니다.ㅠㅜ

    트라이얼로 테스트 해보시고 최신버전으로 업그레이드하셔서 컴포넌트 이용하는 것을 추천드립니다.

    그리고 모바일 개발 하시려면 잦은 모바일 OS업데이트로 델파이 버전도 빠르게 올라가기 때문에 업데이트 서브스크립션을 구매하시는 것이 아주 좋습니다.
    http://www.devgear.co.kr/products/rad-studio/update-subscription/

  7. Blog Icon
    정재용

    비콘과 미디어플레이어 경고 알람 기능이 포함된 소스는 구할 수 없을까요?

    비콘만 구현하면 잘 돌아가고,

    미디어만 구현해도 잘 돌아가는데,

    간단하게 섞을라 치면 어플이 안드로이드핸드폰에서 실행하자마자 꺼지네요.

    휴대폰 리소스 문제일까요?

    비교해보고싶어서 그런데 사용예제를 git으로 얻을 수 있을까 여쭤봅니다.

  8. 이 따라하기가 비콘과 미디어플레이어를 이용하고 있습니다.
    혹시 따라하기로 잘 안되시면 아래 깃헙링크에서 다운로드받아 테스트해보세요.
    (단, 배포: Project > Deployment 부분에서 파일 배포 부분을 잘 검토해 보세요.)
    https://github.com/devgear/RADStudioMobileIoTEdu/tree/master/20150828/BeaconDemo3

  9. Blog Icon
    박문수

    안녕하세요 이번에 비콘을 구매하여서 위험지역 경보 앱을 따라 제작하며

    추천해주신 비콘중에 Gimbal 제품을 구매하여 테스트 하는데 이 제품은 UUID가 따로 없는데

    혹시 이 제품으로 연동하는 방법을 알 수 있을까요?

  10. 제조사 홈페이지에서 찾아보시거나,
    모바일에서 Beacon Scan 기능을 제공하는 앱을 다운받아 비콘을 스캔해 보시기 바랍니다.
    안드로이드인 경우 Beacon Scanner, 아이폰인 경우 Locate 앱을 이용하면 됩니다.

  11. Blog Icon
    박문수

    답변 감사드립니다.
    이야기 해주신 어플은 스캔이 안되어서 다른 어플(BeaconFinder)로 하니깐 나와서 UUID를 입력 후 어플을 돌리면 비콘이 안잡히고 있습니다.
    그래서 다시 UUID 확인을 하면 특정위치 한자리가 변경이 되고 있습니다.
    혹시 이게 왜 이런지 아시는거 있으신가요??

  12. 이 현상은 비콘 스캐너 앱을 의심해봐야 할것 같네요.
    다양한 앱으로 시도해보시기 바랍니다.

    그리고 비콘 제조사 홈페이지에서 UUID를 확인해보시구요.
    제조사에서 제공하는 전용 앱이 있다면 해당 앱으로도 탐색해보세요.

  13. Blog Icon
    이원진

    안녕하세요? 좋은 자료 감사합니다.
    제가 하고자 하는것은 비콘을 자동으로 인식 하고자 하는데..
    보편적으로 매장내에서 비콘을 활용하고자 할경우에 uuid를 입력하는 절차가 있으면 곤란하고요
    uuid를 직접 입력하지 않고 uuid정보를 가져올수 있는 방법은 없을까요?

  14. Blog Icon
    MG

    다음 강의도 올려주세요~

  15. Blog Icon
    박한수

    안녕하세요 이 글을 읽고 비콘이 멀어질때 알람이 울리는 식으로 응용을 해보았습니다. 그리고 비콘을 하나에서 세개로 늘려서 해보았는데

    각 비콘에 동일하게 스위치를 따로 달아서 진행하였고 추가되는 비콘들은

    procedure TForm1.Beacon2BeaconEnter(const Sender: TObject;
    const ABeacon: IBeacon; const CurrentBeaconList: TBeaconList);
    begin
    GBeacon :=ABeacon;
    end;

    이처럼 GBeacon이나 HBeacon으로 선언하여 이벤트를 만들었습니다. 알람부분에서도 타이머를 추가하여 변수 (FIsWarning, StopWarning,FDangerAreaOutSecs 등)도 GBeacon을 위한 새로운 변수(GIsWarning, GDangerAreaOutSecs 등)로 따로 선언을 해 두었습니다.

    이렇게 하니 하나의 스위치를 켜서 할때는 각각 모두 잘 되는데 스위치를 여러개를 켜면 하나의 비콘만 인식을 하고 다른것은 인식을 못하였습니다. 혹시 어디가 잘못되었는지 알 수 있을까요?

  16. Blog Icon
    EH

    4번 항목에
    'lblDistance'라는 오브젝트가 없어서 그러는데요.
    그걸 추가하려면 어떻게 해야하나요?

  17. lblDistnace는 TLabel의 이름(Name)을 lblDistance라고 변경한 것입니다.

  18. Blog Icon
    정진희

    안녕하세요 안드로이드 스튜디오 환경 또는 이클립스에서 어플을 제작하려고 합니다.
    비콘은 아두이노를 이용하여 제작하였고, 이제 제작한 비콘에서 거리 정보를 핸드폰으로 받아오는게 지금 해결해야하는 단계입니다.
    근데 어플을 만들어보는게 처음이라...
    위에 글을 읽어보니까 개발도구로 델파이를 사용하셨고 비콘 컴포넌트라는 것을 사용하셨다고 나와있는데
    혹시 저기 나와있는 비콘 컴포넌트는 델파이에서만 사용할 수 있는건가요?
    아니면 안드로이드 스튜디오 또는 이클립스에서 동일하게 사용할 수 있는건가요?
    감사합니다!

  19. 위 글에서 설명하는 비콘 컴포넌트는 델파이에서 사용해야 합니다.

    델파이 앱개발도 한번 검토해 보세요^^

    데브기어 사물인터넷 1일 교육과정에서 비콘 연동 등을 포함한 실습위주 교육을 진행하고 있습니다.
    하루만에 완성된 앱을 돌려보고 가져갈 수 있습니다.
    http://devgear.co.kr/edu

[XE8] 모바일 앱에서 클래식 블루투스를 이용해 데이터 전송하기

2015.07.27 18:16

지난 7월 24일 사물인터넷 앱 개발 with RAD Studio 교육을 진행했습니다.

교육이 끝나고 한 교육생분이  "클래식 블루투스로 데이터 전송이 잘되지 않아요."라는 질문을 받고 테스트한 내용 공유합니다.


클래식 블루투스 기능 테스트는 RAD Studio 기본 샘플 중 "Classic Bluetooth Basic app"으로 진행했습니다.

Classic Bluetooth Basic app

RAD Studio XE8 사용자는 다음 경로에서 샘플을 확인할 수 있습니다.

C:\Users\Public\Documents\Embarcadero\Studio\16.0\Samples\Object Pascal\Mobile Samples\Device Sensors and Services\Bluetooth\Classic Bluetooth Basic app


이 샘플은 다음 기능을 포함하고 있습니다.

  • 하나의 앱에서 서버(서비스 생성)와 클라이언트 역할을 모두 제공합니다.
  • 장비를 탐색하고, 페어링하고, 서비스를 탐색합니다.
  • 연결된 서비스로 데이터를 전송하고, 서비스는 데이터를 수신합니다.

❑ 다른 서비스에 접속 시 주의할 점과 처리방법

이 샘플은 문자열 전송 서비스를 목적으로 구현되어 있습니다.(클라이언트 소켁 생성 시 문자열 전송 서비스의 GUID(상수)를 이용해서 클라이언트 소켓을 생성합니다.)

다른 서비스와 접속하도록 하려면 선택한 서비스의 GUID를 이용해 클라이언트 소켓을 생성하도록 수정해야 합니다..


위 내용을 적용하기 위해 아래와 같이 샘플 코드를 변경했습니다.


1, 선택한 서비스의 GUID를 보관하기 위한 변수 선언

서비스 콤보박스(ComboBoxService)에서 선택한 서비스의 GUID를 보관하기 위해 private 영역에 변수를 선언

FServiceGUID: string;


2, 서비스 콤보박스 변경 시 변수에 서비스 GUID 할당

ComboBoxService의 OnChange 이벤트에 선택한 서비스의 GUID를 변수에 할당하도록 구현

procedure TForm1.ComboBoxServicesChange(Sender: TObject);
var
  itemText: string;
begin
  if ComboBoxServices.ItemIndex >= 0 then
  begin
    itemText := ComboBoxServices.Items[ComboBoxServices.ItemIndex];
    FServiceGUID := Copy(itemText,
                          Pos('--> ', itemText) + Length('--> '),
                          Length(itemText));
  end;
end;


3, 선택한 서비스의 GUID로 클라이언트 소켓 생성하도록 수정

상수(ServiceGUI)로 선언된 문자열 전송 서비스 GUID 대신 선택한 서비스의 GUID로 클라이언트 소켓을 생성하도록 아래와 같이 수정

procedure TForm1.SendData;
var
  ToSend: TBytes;
  LDevice: TBluetoothDevice;
begin
  if (FSocket = nil) or (ItemIndex <> ComboboxPaired.ItemIndex) then
  begin
    if ComboboxPaired.ItemIndex > -1 then
    begin
      LDevice := FPairedDevices[ComboboxPaired.ItemIndex] as TBluetoothDevice;
      // [데브기어] 상수로 정의된 서비스 GUID 대신 ComboServices의 GUID 값으로 변경
//      DisplayR.Lines.Add(GetServiceName(ServiceGUI));
      DisplayR.Lines.Add(GetServiceName(FServiceGUID));
      DisplayR.GoToTextEnd;
      // [데브기어] 상수로 정의된 서비스 GUID 대신 ComboServices의 GUID 값으로 변경
//      FSocket := LDevice.CreateClientSocket(StringToGUID(ServiceGUI), False);
      FSocket := LDevice.CreateClientSocket(StringToGUID(FServiceGUID), False);
      if FSocket <> nil then
      begin
// 생략


테스트 환경과 결과

❑ 테스트 환경

테스트는 아래 그림과 같이 블루투스 모듈과 블루투스 모듈을 USB로 연결할 수 있는 USB-TTL 모듈을 이용해 블루투스 데이터를 ComPort로 읽어 오도록 구성했습니다.(관련 장비는 다모아 시스템(www.dma.kr)의 최낙구 대표님께서 제공해 주셨습니다.)


테스트에 사용한 모듈은 아래와 같습니다.

❑ 모바일 앱 - 블루투스 장비와 연결, 데이터 전송


블루투스 장비와 페어링

블루투스 장비와 연결하기 위해서는 페어링 과정이 필요합니다.

[Discover devices] 버튼으로 주변의 장비를 탐색하고, 탐색한 장비 선택 후 [Pair] 버튼으로 페어링 시도합니다.


이미 페어링 되어 있는 장비라면 위 과정을 생략할 수 있습니다.(위 페어링 과정은 OS에서 제공하는 페어링 기능을 이용할 수도 있습니다.)

블루투스 장비로 데이터 전송

페어링된 장비를 선택 후 [Services] 버튼을 누르면 장비에서 제공하는 서비스를 가져올 수 있고, 서비스를 선택 후 [Service Demo] 탭에서 [Send text to] 버튼으로 서비스로 문자열을 전송할 수 있습니다.

COM Port 소프트웨어

위 과정을 통해 모바일에서 전송한 데이터는 블루투스 모듈을 통해 ComPort로 데이터를 수신할 수 있습니다.




저작자 표시 비영리 동일 조건 변경 허락
신고

험프리.김현수 파이어몽키 XE8, 블루투스, 클래식 블루투스

  1. 베를린 기준 샘플 컴파일 시 Permission Denied.
    pm에서 블루투스 권한이 하나 안들어가 있어서 다시 부여하고..
    컴파일해서 페어링, 서비스 탐색까지 확인되나
    시리얼 전송 시 Java.IOException 나옵니다.
    해당 샘플에 문제가 있어 보입니다.

    다른 아두이노, 라즈베리, AVR 같은 임베디드 단말이나 마켓에 있는 다른 앱으로는 BT와 시리얼 통신이 잘 이루어집니다.

    테스트 환경은 안드로이드 4.4.2 6.0.1 두가지 입니다.

  2. 글쎄요. 샘플에 문제가 있다고 단정지으면 답이 없어질것 같습니다.(단, 어느 지점이 문제가 있다고 정확한 지적은 발전될 수 있습니다^^)

    저도 처음 시도할때 시행착오를 많이 겪었는데요. 결과적으로 보면 페어링과 연결(GUID)이 잘못된 부분을 찾아내서 해결하니 정상 연결 및 데이터 송수신을 확인할 수 있었습니다.

    문제가 있으시다고 보시면 디버그 모드로 실행하셔서 문제를 확인하고, 문제를 해결하거나 피해가는 형태로 개발해 보시면 반드시 해결할 수 있을 겁니다.

모바일 앱 실행 시 검은화면이 표시되는 경우 조치방법

2015.07.02 16:39

모바일 기기에 앱을 넣고 실행해보면 가끔 어찌된 영문인지 앱 실행 시 검은화면이 표시되고 더이상 구동되지 않는 경우가 발생합니다. 

검은화면은 앱 실행 시 오류가 발생한 경우 표시됩니다.


실행 시 발생하는 오류는 다양하지만 자주 발생하는 이슈는 아래와 같습니다.

  • FormCreate, FormShow 이벤트에 작성한 코드에서 오류 발생
  • 컴포넌트 Enable(또는 Active) 속성을 True로 설정 후 실행하면 실행 시 활성화 되고 활성화 시 오류 발생 가능
  • 필요한 라이브러리가 함께 배포되지 않은 경우
  • (안드로이드 경우)권한이 없는 기능을 사용한 앱이 구동 시 오류 발생

위 내용의 조치방법은 아래와 같습니다.

1, FormCreate(또는 FormShow) 이벤트에 작성한 코드에서 오류 발생

FormCreate 또는 FormShow 이벤트핸들러에 작성한 코드에서 오류가 발생하면 구동 시 검은 화면이 발생합니다.

오류가 발생하지 않도록 코드를 점검하고, 예외처리(try..except)를 추가하기 바랍니다.


또, 라이프사이클 이벤트를 이용해 앱 완전 구동 후 작업을 수행할 수 있습니다.

2, 컴포넌트 Enable(또는 Active)속성을 True로 설정 후 실행하면 실행 시 활성화 되고 활성화 시 오류 발생 가능

폼위에 올려놓은 컴포넌트도 내부적으로는 앱이 실행될때 코드가 실행됩니다. 특히 Enable과 Active 속성을 갖는 컴포넌트의 경우 앱 실행 시 활성화됩니다. 이때 컴포넌트 활성화에 필요한 정보가  설정되지 않은 경우 오류가 발생합니다.

예를 들어 DB 연결 시 DB의 경로가 플랫폼의 경로로 설정되지 않았다면, 오류가 발생할 수 있습니다.


이 경우, 앱이 구동된 이후 Enabled, Active 속성을 True로 변경하도록 처리하기 바랍니다.

3, 필요한 라이브러리가 함께 배포되지 않은 경우 오류가 발생할 수 있습니다.

데이터 연결 등 외부 라이브러리를 이용하는 경우, 외부 라이브러리 배포를 하지 않고 앱을 실행하면 구동 시 오류가 발생합니다.


이 경우, 배포관리자(Project > Deployment) 화면에서 필요한 라이브러리를 추가하기 바랍니다.

그리고, 플랫폼별 배포경로를 다음 링크에서 확인해 Remote Path를 설정하기 바랍니다.

팁으로 데이터 엑세스 관련된 라이브러리 파일은 Add Featured Files 기능을 이용해 추가할 수 있습니다.


4, (안드로이드 경우)권한이 없는 기능을 사용한 앱이 구동 시 오류가 발생할 수 있습니다.

블루투스 컴포넌트를 폼에 올리고 안드로이드에서 앱을 실행하려면 블루투스 권한을 설정해야 합니다.


사용 권한은 프로젝트 옵션(Project > Option > Uses Permissions)에서 설정할 수 있습니다. 


권한에 대한 자세한 정보는 엠바카데로 기술문서를 참고하세요.

5, 기타 오류가 발생할 수 있는 경우

멀티-디바이스 뷰를 추가 후 추가한 뷰에 설정 값을 잘 못 넣은 경우

위 그림과 같이 멀티-디바이스 디자이너는 여러가지 플랫폼과 해상도에 맞는 디바이스 뷰를 추가해 폼 디자인을 다르게 구성할 수 있습니다.


간혹, 디자인 뿐 아니라 연결 컴포넌트 등의 속성을 추가한 뷰(위 그림에서 Android 5" Phone)에 잘못된 설정을 하고, Master 뷰에서 원인을 찾을 경우 쉽게 못찾는 경우가 있습니다.


위 문제의 해결책은 되도록, 연결 등의 Non-visual 컴포넌트는 데이터모듈에 추가하도록 합니다. 부득이 폼에 추가한 경우 Master 뷰에서 속성을 변경 해 추가한 뷰에서 Master의 속성을 상속받아 실행되도록 합니다.

저작자 표시 비영리 동일 조건 변경 허락
신고

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