"나의 도서관" 앱 개발 따라하기
❑ 앱 소개
- 감명깊게 읽은 도서 정보를 기록하는 앱입니다.
- 세미나 발표와 샘플소스 제공을 위해 다소 주제와 맞지 않은 기능이 포함되어 있습니다.
- 해당 앱은 RAD Studio XE7(또는 AppMethod 1.15)로 개발 후 하나의 소스코드로 안드로이드와 iOS 앱을 동시에 개발하였습니다.
따라하기를 통해 습득할 수 있는 기술
- 앱 개발의 전반적인 흐름을 따라하며 익혀 볼 수 있습니다.
- 기본적인 UI 컨트롤 사용법과 속성 사용법
- 화면 구성에 도움이 되는 샘플데이터 생성(프로토타입 데이터 소스)
- 화면요소와 데이터를 (소스코드 없이)시각적으로 연결하는 기술(라이브바인딩)
- 이미 구현된 기능을 재활용할 수 있는 프래임 활용방법(TFrame)
- 앱에 포함하여 배포할 수 있는 임베디드 데이터베이스 사용법(IBLite)
- 앱에서 데이터 연결 후 입력/수정/삭제 과정
- 사용자화면 만드기, 기능 구현하기
- 실제 데이터베이스 연결, 배포
따라하기 1 - 사용자 화면 만들기와 기능 구현하기
- 빈 멀티-디바이스 앱 하나를 선택합니다.
- 사용자들에게 보여질 화면을 만듭니다.
- 샘플 데이터 추가 후 화면요소와 연결해 화면 표시 후 테스트하기
- 사용자의 조작에 동작하는 기능코드를 작성합니다.
- 전화걸기, 사진, 웹페이지 표시 기능 추가
- 사용자화면 완성! 테스트 하기
1, 빈 멀티-디바이스 앱 하나를 선택합니다.
- File > New > Multi-Device Application - Delphi 메뉴를 선택 하고, Blank Application을 선택해 프로젝트를 생성합니다.
- File > Save all 메뉴를 선택하고 유닛이름은 "MainForm.pas"로 프로젝트 이름은 'BookLogFMX"로 저장합니다.
2, 사용자들에게 보여질 화면을 만듭니다.
탭컨트롤
도서 목록
|
상위 오브젝트 | 오브젝트 | 속성 | 값(또는 설명) |
TabItem1 | ToolBar1 |
|
|
ToolBar1 | Label1 | Align | Contents |
StyleLookup | toollabel | ||
Text | 나의 도서관 | ||
TextSettings.HorzAlign | Center | ||
btnNewItem (TButton) | Name | btnNewItem | |
Align | Right | ||
StyleLookup | additemButton | ||
Tabitem1 | ListView1 | Align | Client |
ItemApperance.ItemAppearance | ImageListItemBottomDetail | ||
ItemApperance.ItemHeight | 88 | ||
ItemApperance.ItemObjects .Image.Width / Height | 60 / 80 | ||
ItemApperance.ItemObjects .Text.WordWrap | True |
도서 상세보기
상위 오브젝트 | 오브젝트 | 속성 | 값(또는 설명) |
TabItem2 | ToolBar2 |
|
|
ToolBar2 | Label2 | Align | Contents |
StyleLookup | toollabel | ||
Text | 제목 | ||
TextSettings.HorzAlign | Center | ||
TextSettings.WordWrap | False | ||
btnBackList | Name | btnBackList | |
Align | Left | ||
StyleLookup | arrowlefttoolbutton | ||
btnDetail (TButton) | Name | btnDetail | |
Align | Right | ||
StyleLookup | detailstoolbutton | ||
TabItem2 | Layout1 | Align | Client |
Layout1 | Panel1 | Align | Top |
Height | 116 | ||
Margins | 8, 8, 8, 8 | ||
Panel1 | ShadowEffect1 | Distance | 2 |
ShadowColor | Gray | ||
Rectangle1 | Image1 | Align | Left |
Margins | 8, 8, 8, 8 | ||
Width | 75 | ||
Rectangle1 | Layout2 | Align | Client |
Layout2 | lblTitle(TLabel) | Name | lblTitle |
Align | MostTop | ||
AutoSize | True | ||
Margins | 0, 8, 3, 16 | ||
StyledSettings.Style | False | ||
TextSettings.Font.Size | 16 | ||
TextSettings.Font.Style.fsBold | True | ||
lblAuthor(TLabel) | Name | lblAuthor | |
Align | Top | ||
Margins | 3, 12, 3, 10 | ||
Layout1 | ListBox1 | Align | Client |
ListBox1 | StyleLookup Text | listboxitemnodetail 출판사, 연락처, 사이트, 감상평 | |
ListBoxitem4 | Height | 150 | |
ListBoxItem1 | lblPublisher(TLabel) | Align Margins.Left (4개 공통) | Client |
ListBoxItem2 | lblPhone(TLabel) | ||
ListBoxItem3 | lblWebSite(TLabel) | ||
ListBoxItem4 | lblComment(TLabel) | ||
ListBoxItem2 | lblPhone | HitTest | Ture |
ListBoxItem3 | lblWebSite | HitTest | True |
ListBoxItem4 | lblComment | TextSettings.VertAlign | Leading |
Tabitem2 | OverflowMenu(TListBox) | Name | OverflowMenu |
Position.X / Position.Y | Button3 아래로 이동 | ||
Width / Height | 88 / 96 | ||
OverflowMenu | lstItemModify, lstItemDelete (TListBoxItem) | Name | lstItemModify / lstItemDelete |
StyleLookup | listboxitemstyle | ||
Text | 수정 / 삭제 | ||
TextSettings.HorzAlign | Center | ||
ShadowEffect2 |
새로운 도서 추가
|
상위 오브젝트 | 오브젝트 | 속성 | 값(또는 설명) |
TabItem3 | ToolBar3 |
|
|
ToolBar3 | Label3 | Align | Contents |
StyleLookup | toolbutton | ||
Text | 도서 추가 | ||
TextSettings.HorzAlign | Center | ||
btnCancel (TButton) | Name | btnCancel | |
Align | Left | ||
StyleLookup | backtoolbutton | ||
Text | 취소 | ||
btnSaveitem (TButton) | Name | btnSaveItem | |
Align | Right | ||
StyleLookup | donetoolbutton | ||
Text | 저장 | ||
TabItem3 | vsbEditForcus (TVertScrollBox) | Name | vsbEditFocus |
Align | Client | ||
vsbEditForcus | lytContentsNew(TLayout) | Name | lytContentsNew |
Align | Client | ||
Layout3 | Layout4 | Align | Top |
Height | 113 | ||
Layout4 | Rectangle2 | Align | Center |
Fill.Kind | None | ||
Width / Height | 100 / 100 | ||
Rectangle2 | imgNewItem(TImage) | Name | imgNewItem |
Align | Client | ||
Margins | 2, 2, 2, 2 | ||
Layout3 | ListBox2 | Align | Client |
ListBox2 | ListBoxItem7 ~ 12 (6개 추가) | StyleLookUp | listboxitemnodetail 제목, 저자, 출판사, 연락처, 사이트, 감상평 |
ListBoxitem12 | Height | 100 | |
ListBoxitem7 | edtTitle(TEdit) | Align Margins | Client 7, 80, 5, 7 |
ListBoxitem8 | edtAuthor(TEdit) | ||
ListBoxitem9 | edtPublisher(TEdit) | ||
ListBoxitem10 | edtPhone(TEdit) | ||
ListBoxitem11 | edtWebSite(TEdit) | ||
ListBoxitem12 | mmoComment(TMemo) | ||
mmoComment | TextSettings.WordWrap | True |
3, 샘플 데이터 추가 후 화면요소와 연결해 화면 표시 후 테스트하기
화면 테스트를 위해 샘플(프로토타입) 데이터를 데이터 모듈에 추가합니다. 뒤에서 샘플 데이터를 실제 데이터베이스로 변경합니다.
데이터 엑세스 기능을 구현하기 위해 데이터모듈 추가
- File > New > Other 메뉴를 선택 해 "New Items" 대화상자를 표시합니다.
- 왼쪽 트리메뉴에서 Object Pascal Projects > Object Pascal Files를 선택합니다.
- Data Module을 선택 하고 OK 버튼을 누릅니다.
- 데이터 모듈이 추가되면 File > Save 메뉴를 누르고 "DataAccessModule.pas"로 저장합니다.
- Object Inspect(속성창)에서 Name 속성을 dmDataAccess로 변경합니다.
테스트용 샘플 데이터 추가
- 데이터모듈에 TPrototypeDataSource를 추가합니다.
- PrototypeDataSource1의 RecordCount를 30으로 변경합니다.
- PrototypeDataSource1을 더블클릭 후 Add New 버튼으로 위 그림을 참고해 항목 5개를 추가합니다.
샘플데이터와 화면요소 연결하기
- MainForm으로 돌아와 위에서 추가한 데이터 모듈 사용하기 위해 File > Use unit 메뉴를 선택하고 "DataAccessModule.pas" 파일을 선택 후 OK 버튼을 누릅니다.
- View > LiveBindings Designer 메뉴를 통해 라이브 바인딩 디자이너를 표시하고, 아래 그림을 참고해 화면요소에 데이터를 연결합니다.(두 항목간 마우스로 드래그해 연결)
도서 목록
도서 상세보기
새로운 도서 추가
연결을 마치면 Live Bindings Designer를 닫습니다.(창 우측 상단 X 버튼)
4, 사용자의 조작에 동작하는 기능코드를 작성합니다.
화면(탭)이동 기능 구현
- 폼에 TActionList(ActionList1) 컴포넌트를 추가합니다.
- ActionList1을 더블클릭 후 Add Action > New Standard Action 메뉴를 선택 합니다.
- Tab > TChangeTabAction 항목 선택 후 OK 버튼을 눌러 추가합니다.
코드 에디터를 열고(폼 디자이너에서 F12 버튼) 선언부(코드 상단) private 영역에 아래 코드를 입력합니다.
private { Private declarations } procedure GotoList; procedure GotoDetail; procedure GotoNew; public { Public declarations } end;
- 구현부(선언부 입력 후 Ctrl + Shift + C 단축키를 누르면 구현부의 구조가 자동 완성됩니다.)에 아래의 코드를 입력합니다.
procedure TForm1.GotoDetail; begin ChangeTabAction1.Tab := TabItem2; ChangeTabAction1.ExecuteTarget(nil); end; procedure TForm1.GotoList; begin ChangeTabAction1.Tab := TabItem1; ChangeTabAction1.ExecuteTarget(nil); end; procedure TForm1.GotoNew; begin ChangeTabAction1.Tab := TabItem3; ChangeTabAction1.ExecuteTarget(nil); end;
도서 목록 탭에서 ListView1의 OnItemClick 이벤트에 아래의 코드를 입력합니다. 3
procedure TForm1.ListView1ItemClick(const Sender: TObject; const AItem: TListViewItem); begin GotoDetail; end;
- 위 방식과 같이 아래의 표를 참고해 이벤트 핸들러 코드를 입력합니다.
오브젝트
이벤트
소스코드
btnNewItem
OnClick
GotoNew;
btnBackList
OnClick
GotoList; btnCancel OnClick
GotoList; btnSaveItem
OnClick
GotoList; 폼을 선택하고 OnCreate 이벤트에 아래 코드를 입력합니다.(실행 시 탭을 감추고 첫번째 탭을 표시하는 코드입니다.)
procedure TForm1.FormCreate(Sender: TObject); begin TabControl1.TabPosition := TTabPosition.None; TabControl1.TabIndex := 0; end;
도서 상세정보 수정/삭제 팝업메뉴 구현
도서 상세정보 탭의 추가정보 버튼(btnDetail)과, 수정(lstItemModify), 삭제(lstItemDelete) 항목의 OnClick 이벤트에 아래를 참고해 코드를 추가합니다.
procedure TForm1.btnDetailClick(Sender: TObject); begin OverflowMenu.Visible := not OverflowMenu.Visible; if OverflowMenu.Visible then begin OverflowMenu.ItemIndex := -1; OverflowMenu.BringToFront; OverflowMenu.ApplyStyleLookup; OverflowMenu.RealignContent; OverflowMenu.Position.X := Width - OverflowMenu.Width - 5; OverflowMenu.Position.Y := Toolbar2.Height; end; end; // 수정 procedure TForm1.lstItemModifyClick(Sender: TObject); begin OverflowMenu.Visible := False; GotoNew; end; // 삭제 procedure TForm1.lstItemDeleteClick(Sender: TObject); begin OverflowMenu.Visible := False; MessageDlg('해당 정보를 삭제하시겠습니까?', TMsgDlgType.mtWarning, [TMsgDlgBtn.mbYes, TMsgDlgBtn.mbNo], 0, procedure(const AResult: TModalResult) begin if AResult = mrYes then begin ShowMessage('삭제'); end; end); end;
폼을 선택하고 OnCreate 이벤트에 아래 코드를 추가합니다.
procedure TForm1.FormCreate(Sender: TObject); begin TabControl1.TabPosition := TTabPosition.None; TabControl1.TabIndex := 0; OverflowMenu.Visible := False; // 시작 시 팝업메뉴 감추기 end;
입력 시 키보드가 가려지지 않도록 입력박스 위치조정 기능 구현
하단의 입력박스에 입력 시 키보드가 입력박스를 가리는 경우 키보드 위로 입력박스 위치를 조정하는 기능을 구현합니다.
- 선언부 private 영역에 아래 코드를 입력합니다.
private { Private declarations } FKBBounds: TRectF; FNeedOffset: Boolean; procedure CalcContentBoundsProc(Sender: TObject; var ContentBounds: TRectF); procedure RestorePosition; procedure UpdateKBBounds;
- 구현부에 아래의 코드를 입력합니다.
procedure TForm1.CalcContentBoundsProc(Sender: TObject; var ContentBounds: TRectF); begin if FNeedOffset and (FKBBounds.Top > 0) then begin ContentBounds.Bottom := Max( ContentBounds.Bottom, 2 * ClientHeight - FKBBounds.Top); end; end; procedure TForm1.RestorePosition; begin vsbEditFocus.ViewportPosition := PointF(vsbEditFocus.ViewportPosition.X, 0); lytContentsNew.Align := TAlignLayout.Client; vsbEditFocus.RealignContent; end; procedure TForm1.UpdateKBBounds; var LFocused : TControl; LFocusRect: TRectF; begin FNeedOffset := False; if Assigned(Focused) then begin LFocused := TControl(Focused.GetObject); LFocusRect := LFocused.AbsoluteRect; LFocusRect.Offset(vsbEditFocus.ViewportPosition); if (LFocusRect.IntersectsWith(TRectF.Create(FKBBounds))) and (LFocusRect.Bottom > FKBBounds.Top) then begin FNeedOffset := True; lytContentsNew.Align := TAlignLayout.Horizontal; vsbEditFocus.RealignContent; Application.ProcessMessages; vsbEditFocus.ViewportPosition := PointF(vsbEditFocus.ViewportPosition.X, LFocusRect.Bottom - FKBBounds.Top); end; end; if not FNeedOffset then RestorePosition; end;
코드에서 수학함수(Max)를 사용하기 위해 구현부(implementation) uses 절에 System.math 유닛을 추가합니다.
implementation {$R *.fmx} uses DataAccessModule, System.Math;
- 폼(Form1)의 OnFocusChanged, OnVirtualKeyboardShown, OnVirtualKeyboardHidden 이벤트에 아래 코드를 참고해 코드를 추가합니다.
procedure TForm1.FormFocusChanged(Sender: TObject); begin UpdateKBBounds; end; procedure TForm1.FormVirtualKeyboardHidden(Sender: TObject; KeyboardVisible: Boolean; const Bounds: TRect); begin FKBBounds.Create(0, 0, 0, 0); FNeedOffset := False; RestorePosition; end; procedure TForm1.FormVirtualKeyboardShown(Sender: TObject; KeyboardVisible: Boolean; const Bounds: TRect); begin FKBBounds := TRectF.Create(Bounds); FKBBounds.TopLeft := ScreenToClient(FKBBounds.TopLeft); FKBBounds.BottomRight := ScreenToClient(FKBBounds.BottomRight); UpdateKBBounds; end;
폼의 OnCreate 이벤트에 아래 코드를 추가합니다.
procedure TForm1.FormCreate(Sender: TObject); begin TabControl1.TabPosition := TTabPosition.None; TabControl1.TabIndex := 0; OverflowMenu.Visible := False; vsbEditFocus.OnCalcContentBounds := CalcContentBoundsProc; // 추가 end;
5, 전화걸기, 사진, 웹페이지 표시 기능 추가
전화걸기, 사진 촬영, 불러오기, 웹페이지 표시 기능을 추가합니다.
전화걸기 기능 추가하기
전화걸기 기능 사용을 위해 FMX.Platform과 FMX.PhoneDialer 유닛을 구현부(implementation) 유즈절에 추가합니다.
implementation {$R *.fmx} uses DataAccessModule, System.Math, FMX.Platform, FMX.PhoneDialer;
도서 상세보기 탭의 lblPhone(연락처 항목)의 OnClick 이벤트 핸들러에 아래의 코드를 입력합니다.
procedure TForm1.lblPhoneClick(Sender: TObject); var PhoneDlrSvc: IFMXPhoneDialerService; begin if TPlatformServices.Current.SupportsPlatformService(IFMXPhoneDialerService, IInterface(PhoneDlrSvc)) then PhoneDlrSvc.Call(lblPhone.Text); end;
웹페이지 표시 기능, 사진 기능 추가하기
웹페이지 기능과 사진 기능은 이미 구현된 소스코드를 다운받아 구현합니다.
카메라 기능, 웹브라우저 기능이 구현된 프레임 소스코드 를 다운로드 받고 압축 해제 후 프로젝트 파일(BookLogFMX.dpr) 저장경로의 하위에 Frames 폴더를 추가 후 Frames 폴더 안으로 복사합니다.
Project Manager 윈도우에서 프로젝트 이름(BookLogFmx)에 마우스 오른쪽 팝업메뉴를 표시하고 Add... 메뉴를 선택 후 위 1번에서 복사한 "PhotoFrame.pas"를 선택 해 추가합니다.
PhotoFrame 사용을 위해 구현부 uses절에 WebBrowserFrame, PhotoFrame을 추가합니다.
"도서 상세보기" 탭의 lblWebSite(사이트 항목)의 OnClick 이벤트 핸들러에 아래의 코드를 입력합니다.
procedure TForm1.lblWebSiteClick(Sender: TObject); begin TfrWebBrowser.CreateAndShow(Self, lblWebSite.Text); end;
선언부 private 영역에 아래의 메소드를 선언하는 코드를 추가합니다.
procedure ChangeImageEvent(Image: TBitmap);
구현부에 아래의 코드를 추가합니다.
procedure TForm1.ChangeImageEvent(Image: TBitmap); begin imgNewItem.Bitmap.Assign(Image); end;
"새로운 도서 추가" 탭의 imgNewItem(사진)의 OnClick 이벤트 핸들러에 아래 코드를 입력합니다.
procedure TForm1.imgNewItemClick(Sender: TObject); begin TfrPhoto.CreateAndShow(Self, ChangeImageEvent, nil); end;
- 안드로이드 백버튼을 누를때 기능이 닫히게 하기위해 폼(Form1)의 OnKeyUp 이벤트에 아래 코드를 입력합니다.
procedure TForm1.FormKeyUp(Sender: TObject; var Key: Word; var KeyChar: Char; Shift: TShiftState); begin if Key = vkHardwareBack then begin if Assigned(frPhoto) then begin frPhoto.CloseFrame; Key := 0; end; if Assigned(frWebBrowser) then begin frWebBrowser.CloseFrame; Key := 0; end; end; end;
6, 사용자화면 완성! 테스트 하기
1차 사용자화면 개발완료되었습니다.
- 배포를 원하는 플랫폼을 선택하고 Run > Run Without Debugging 메뉴를 통해 배포 및 실행합니다.
(안드로이드 개발환경 설정은 앱메소드 튜토리얼 동영상을 통해 확인할 수 있습니다.)
"나의 도서관 앱" 개발 따라하기 - (2) 데이터베이스 만들기, 실제 데이터 연결
개발도구
- RAD Studio 소개 & 다운로드 - http://devgear.co.kr/products/rad-studio/
관련글
- "나의 도서관 앱" 개발 따라하기 - (2) 데이터베이스 만들기, 실제 데이터 연결
- 기능을 별도의 프레임(TFrame)으로 분리해 재활용하기(예제포함)
- 소스코드 - BookLog-Prototype 디렉토리 참조
참고
- [동영상] 앱메소드 튜토리얼(한글)
- [WIKI] 앱메소드 도움말(한글번역)
- [Community] 엠바카데로 커뮤니티 사이트
- [Mobile tutorial] 다양한 스타일을 갖는 버튼 컴포넌트 사용하기(영문)
- [Mobile tutorial] 사진 찍기, 앨범에서 불러오기, 사진 조정하기(영문)
- [Mobile tutorial] 모바일 장치에서 전화걸기(영문)
- [Mobile tutorial] 웹 브라우저 사용, 적합한 가상키보드 표시(영문)
- TabItem 추가는 TabControl 더블클릭 후 나오는 Items Designer 대화상자에서 TTabItem 선택 후 [Add Item] 버튼을 눌러 추가 가능 [본문으로]
- ListBoxItem 추가는 ListBox컴포넌트 더블클릭 후 나타나는 Items Designer 대화상자에서 TListBoxItem을 선택 후 [Add Item] 버튼을 눌러 추가 가능 [본문으로]
- Object Inspector 창의 Event 탭으로 이동 후 OnItemClick 글자 옆의 공백을 더블클릭하면 이벤트 핸들러 코드가 자동추가 [본문으로]