Video Clip B01 소스 분석_앱플러스폼(ApplusForm, AppForm)
ApplusForm에서 제공하는 Templete 중 Video Clip B01에 대한 강좌입니다.
개요
홈페이지에서 소개하는 내용입니다.
Video Clip Template B01 Type Video Clip Template은 동영상 기반의 서비스 제공에 적합한 형식입니다.
* 주요기능
동영상 리스트, 동영상 플레이, 전체화면 플레이 등
* 활용분야
요가, 스포츠, 동영상 학습 강의 등
* 특징
유투브 동영상 스트리밍, 즐겨찾기, 동영상 플레이어 오버레이 카메라뷰 노출 등
* 최종 업데이트 일자 : 2013.08.12 / 지원 OS : Android 2.3 ~ , iOS 5.0 ~
VideoClipB01은 플레이할 영상 목록을 출력하고, 주로 보는 영상을 보관함에 즐겨찾기 하는 기능을 제공합니다.
MOML에 동영상플레이어인 VIDEO가 있지만 Youtube영상을 보는 기능은 Web 동영상 플레이어를 이용하였습니다.
보관함 관리를 위해 DB를 사용했고, 동영상을 보면서 따라하는 자신의 모습을 모니터링하기 위해 전면 카메라를 사용합니다.
특정 주제의 동영상을 목록으로 관리하고 보여주는 애플리케이션을 만들고 싶다면 유용합니다.
홈페이지에서도 스크린샷을 볼 수 있지만 어떤 화면을 제공하는지 잠시 보겠습니다.
DATA.
목록에 Thumbnail 이미지를 구성하기 위해서는 목록을 담고 있는 파일을 생성해야 합니다.
목록 데이터는 /moml/data에 있습니다.
/moml/data에는 목록에서 사용할 스크린샷 이미지와 목록을 구성하는 listData.xml이 있음을 볼 수 있습니다.
<?xml version="1.0" encoding="UTF-8"?>
<data>
<contatct>
<id>1</id>
<title>Miss A Suzy Makeup Tutorial//수지 화장법</title>
<subtitle>수지 화장법 </subtitle>
<videopath>aTsFThe1JDE</videopath>
</contatct>
<contatct>
<id>2</id>
<title>소셜블로그 : 새내기 화장법 </title>
<subtitle>메이크업 아티스트 옥충길 팀장이 제안하는 새내기 화장법. </subtitle>
<videopath>ONm9HO_yjJQ</videopath>
</contatct>
...중략...
</data>
listData.xml에는 목록에 들어갈 내용으로 구성됩니다.
thumbnail이미지도 적당히 만들어서 파일이름을 videopath와 동일하게 만들어 둡니다.
소스.
늘 그렇듯이 시작은 /moml/ui/start.xml입니다.
<MOML version="1.1.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="http://www.applusform.com/xsd/moml_ui.xsd">
<UI>
<UILAYOUT landscape="1280,720">
<NAVIGATIONCONTAINER id="start_navigationContainer" layout="0,0,1280,720" selectedItem="main" defaultImg="#ffffff">
<VIEWITEM id="main" src="main.xml" transitionInEffect="move" transitionOutEffect="move" >
<VIEWITEM id="vcb01Info" src="vcb01Info.xml" transitionInEffect="move" transitionOutEffect="move" />
<VIEWITEM id="vcb01Start" src="vcb01Start.xml" transitionInEffect="move" transitionOutEffect="move" >
<VIEWITEM id="vcb01Play" src="vcb01Play.xml" transitionInEffect="move" transitionOutEffect="move" />
</VIEWITEM>
<VIEWITEM id="vcb01OfMe" src="vcb01OfMe.xml" transitionInEffect="move" transitionOutEffect="move" >
<VIEWITEM id="vcb01Play" src="vcb01Play.xml" transitionInEffect="move" transitionOutEffect="move" />
</VIEWITEM>
</VIEWITEM>
</NAVIGATIONCONTAINER>
</UILAYOUT>
</UI>
</MOML>
가로모드만 지원하기 때문에 <UILAYOUT>
에는 landscape 해상도만 지정했습니다.
NAVIGATIONCONTAINER만 존재하고 다른 내용은 없으니 넘어가겠습니다.
첫 페이지인 main.xml입니다.
<UI>
<UILAYOUT landscape="1280,720" theme="theme1">
<WINDOW layout="0,0,1280,720" align="relative" defaultImg="/res/m_bg.png" onShow="{function.go_dbInit}">
<IMAGE layout="71,90,855,74" defaultImg="/res/m_title.png"/>
<IMAGE id="flower1" layout="1012,230,15,17" defaultImg="/res/flower1.png" visible="invisible"/>
<IMAGE id="flower2" layout="986,223,22,23" defaultImg="/res/flower2.png" visible="invisible"/>
<IMAGE id="flower3" layout="950,253,32,26" defaultImg="/res/flower3.png" visible="invisible"/>
<IMAGE id="flower4" layout="906,292,70,67" defaultImg="/res/flower4.png" visible="invisible"/>
<IMAGE id="flower5" layout="840,269,60,61" defaultImg="/res/flower5.png" visible="invisible"/>
<IMAGE id="flower6" layout="1016,300,46,40" defaultImg="/res/flower6.png" visible="invisible"/>
<IMAGE id="flower7" layout="849,105,107,109" defaultImg="/res/flower7.png" visible="invisible"/>
<IMAGE id="flower8" layout="746,316,134,127" defaultImg="/res/flower8.png" visible="invisible"/>
<IMAGE id="flower9" layout="677,459,168,151" defaultImg="/res/flower9.png" visible="invisible"/>
<IMAGE id="flower10" layout="230,406,256,237" defaultImg="/res/flower10.png" visible="invisible"/>
<!-- 추가하기를 원하는 메뉴가 있다면 아래에 추가합니다. -->
<WINDOW layout="600,374,980,446" align="linear:vertical|top|center" defaultImg="#00ffffff">
<BUTTON id="m_menu1" themeId="menulist" text="Makeup 시작하기" onClick="{root.start_navigationContainer.selectedItem = 'vcb01Start'}"/>
<BUTTON id="m_menu2" themeId="menulist" text="나만의 Makeup" onClick="{root.start_navigationContainer.selectedItem = 'vcb01OfMe'}"/>
<BUTTON id="m_menu3" themeId="menulist" text="About" onClick="{root.start_navigationContainer.selectedItem = 'vcb01Info'}"/>
</WINDOW>
</WINDOW>
</UILAYOUT>
</UI>
배경이미지와 <BUTTON>
으로 메뉴를 배치했습니다.
id가 flower1~10인 IMAGE는 애니메이션을 위해 배치되었습니다. invisible이므로 처음에는 보이지 않습니다.
Base가 되는 <WINDOW>
에 onShow시 Function이 실행되도록 되어 있습니다.
<FUNCTIONLIST>
<!-- 나만의 Makeup에서 사용하는 DB초기화 -->
<FUNCTION id="go_dbInit">
<FUNCTIONITEM cmd="{db.exec('CREATE TABLE IF NOT EXISTS vcb01( _id INTEGER PRIMARY KEY AUTOINCREMENT, title TEXT NOT NULL, subtitle TEXT NOT NULL, videopath TEXT NOT NULL)')}"/>
<FUNCTIONITEM cmd="{db.exec('SELECT * FROM vcb01')}"/>
<FUNCTIONITEM cmd="{userVariable.vcb01OfMeData = db.result.getXmlData()}"/>
<FUNCTIONITEM condition="{saveVariable.picBefore != '/res/btn_select_camera_tou.jpg' && saveVariable.picBefore != ''}" cmd="{userVariable.beforeVisible = 'visible'}" elseCmd="{userVariable.beforeVisible = 'invisible', saveVariable.picBefore = '/res/btn_select_camera_tou.jpg'}"/>
<FUNCTIONITEM condition="{saveVariable.picAfter != '/res/btn_select_camera_tou.jpg' && saveVariable.picAfter != ''}" cmd="{userVariable.afterVisible = 'visible'}" elseCmd="{userVariable.afterVisible = 'invisible', saveVariable.picAfter = '/res/btn_select_camera_tou.jpg'}"/>
<FUNCTIONITEM cmd="m_menu1.visible=m_menu2.visible=m_menu3.visible='invisible'"/>
<FUNCTIONITEM cmd="function.menuAni" delay="50"/>
<FUNCTIONITEM cmd="flower1.visible=flower2.visible=flower4.visible=flower5.visible=flower6.visible=flower7.visible=flower8.visible=flower9.visible=flower10.visible=flower3.visible='invisible'"/>
<FUNCTIONITEM cmd="function.flowerAni" delay="1500"/>
</FUNCTION>
<!-- 메뉴 애니멘이션 -->
<FUNCTION id="menuAni">
<FUNCTIONITEM cmd="animation.show('m_menu1', 'fade', '', 500, '')"/>
<FUNCTIONITEM cmd="animation.show('m_menu2', 'fade', '', 500, '')"/>
<FUNCTIONITEM cmd="animation.show('m_menu3', 'fade', '', 500, '')"/>
</FUNCTION>
<!-- 배경 꽃무늬 애니메이션 -->
<FUNCTION id="flowerAni">
<FUNCTIONITEM cmd="animation.show('flower1', 'fade', '', 1000, '')"/>
<FUNCTIONITEM cmd="animation.show('flower2', 'fade', '', 1000, '')" delay="200"/>
<FUNCTIONITEM cmd="animation.show('flower3', 'fade', '', 1000, '')" delay="400"/>
<FUNCTIONITEM cmd="animation.show('flower4', 'fade', '', 1000, '')" delay="800"/>
<FUNCTIONITEM cmd="animation.show('flower5', 'fade', '', 1000, '')" delay="1000"/>
<FUNCTIONITEM cmd="animation.show('flower6', 'fade', '', 1000, '')" delay="1200"/>
<FUNCTIONITEM cmd="animation.show('flower7', 'fade', '', 1000, '')" delay="1400"/>
<FUNCTIONITEM cmd="animation.show('flower8', 'fade', '', 1000, '')" delay="1600"/>
<FUNCTIONITEM cmd="animation.show('flower9', 'fade', '', 1000, '')" delay="1800"/>
<FUNCTIONITEM cmd="animation.show('flower10', 'fade', '', 1000, '')" delay="2000"/>
</FUNCTION>
</FUNCTIONLIST>
go_dbInit Function에서는 보관함으로 사용할 DB를 생성한 후 menuAni Function을 실행하고 flowerAni Function을 실행하여 애니메이션 효과를 줍니다.
실행하면 메뉴는 천천히 나타나고 배경에 빨간 꽃이 차례로 나타나는 애니메이션이 실행됩니다.
‘Makrup 시작하기’ 메뉴인 vcb01Start.xml은 타이틀에 이전버튼을 배치하고 목록을 <LIST>
로 출력하고 있습니다.
UI에 특이점은 없으니 Function들을 살펴보겠습니다.
UI :
<BUTTON layout="112,78" defaultImg="/res/btn_list_play.png" pressedImg="/res/btn_list_play_tou.png" onClick="{userVariable.listPosition = xpath.position()+1, function.go_facePlay(xpath.position())}"/>
FUNCTION :
<FUNCTION id="go_facePlay(index)">
<FUNCTIONITEM cmd="{userVariable.listTitle = faceList.items[index].listTitle.text}" />
<FUNCTIONITEM cmd="{userVariable.videoPath = faceList.items[index].videoPath.text}"/>
<FUNCTIONITEM cmd="{userVariable.PlayStatu = 'Start'}"/>
<FUNCTIONITEM cmd="device.log('kh', userVariable.videoPath)"/>
<FUNCTIONITEM cmd="action:go('root.start_navigationContainer', 'vcb01Play')" />
</FUNCTION>
BUTTON을 눌렀을 때 userVariable.listPosition에 xpath의 현재위치에 1을 더한 값을 넣고 go_facePlay function을 호출합니다.
UI는 생성되면서 모든 UI Object가 생성되므로 <LISTLAYOUT>
안에 있는 BUTTON도 data의 개수만큼 반복해서 생성됩니다.
xpath.position()은 LIST에서 현재 몇번째 데이터가 출력되고 있는지를 알려주는 값으로도 활용됩니다.
go_facePlay Function에서 userVariable.listTitle과 userVariable.videoPath는 vcb01Play.xml에서 사용하기 위한 임시변수인데, 여기에 값을 넣을 때 faceList.items[index].listTitle.text라고 되어 있습니다.
faceList는 <LIST>
이고 LIST.items[1~n]으로 각 item들을 직접 접근할 수 있습니다.
LIST.items는 첫번째 item이 1고 xpath.position은 첫번째 값이 0이므로 1을 더해줬습니다.
action:go()는 deprecated function입니다. 이는 root.start_navigationContainer.selectedItem = ’vcb01Play’로 바꿔주는 것이 좋습니다.
Youtube동영상을 play하는 vcb01Play.xml을 보겠습니다.
<WEBVIEW id="video" layout="5,5,1270,710" loadSrc="default.htm" defaultImg="#000000" clientControl="false"/>
<WINDOW id="camWin" layout="870,300,400,400" align="relative" defaultImg="#50000000" visible="invisible">
<CAMERA id="cam" layout="5,5,390,390" type="front" savePath="storage:camera"/>
<WINDOW layout="0,0,400,400" defaultImg="#00ffffff" onFlickLeft="{function.toLeft}" onFlickRight="{function.toRight}"/>
</WINDOW>
<WEBVIEW>
위에 <CAMERA>
를 배치하고 있고 카메라의 위치를 바꿀 수 있도록 onFlickLeft와 onFLickRight이벤트를 처리하는 <WINDOW>
를 카메라위에 배치했습니다.
카메라는 화면의 일부분만을 차지하도록 개발하기 어려워 대개 전체 화면으로 개발됩니다만, MOML에서는 손쉽게 처리가 가능합니다.
<FUNCTION id="toLeft">
<FUNCTIONITEM condition="camWin.left != 5" cmd="camWin.visible='invisible', camWin.left=5, animation.flyIn('camWin', 865, 0, 100, '', 500, '')"/>
</FUNCTION>
<FUNCTION id="toRight">
<FUNCTIONITEM condition="camWin.left != 870" cmd="camWin.visible='invisible', camWin.left=870, animation.flyIn('camWin', -865, 0, 100, '', 500, '')"/>
</FUNCTION>
toLeft, toRight function로 카메라를 좌,우로 이동시키는 애니메이션을 적용했습니다.
부드럽게 이동하는 것을 볼 수 있습니다.
Android에서는 이동중에는 카메라가 까맣게 보이지만 iOS에서는 카메라도 켜져 있는 상태로 이동하는 것을 볼 수 있습니다.
OS별로 차이가 있는 부분입니다.
MagazineB01 템플릿 소스분석 및 소개를 마칩니다.