 |
|
 |
|
 |
 |
|
|
|
|
|
|
| Flash 는 별개의 runtime 프로그램으로 Google 과 같은 검색엔진을 통한 인덱싱 문제에 상당한 어려움을 가지고 있다. 이것은 일반적인 flash 사이트일 경우 검색엔진을 통한 노출이 사실상 불가능하다는 의미이다. 이는 flash 파일의 내부를 분석하여 정보를 추출하는 알고리즘이 개발되지 않는 한 이러한 이슈는 계속 될 것으로 보인다.
위 문제의 대한 완벽한 해결방안은 현재로서는 없다. 하지만 html 페이지와 같은 텍스트 기반의 인덱싱 수준을 기대하기는 어렵지만 어느 정도 대체할 만한 방법론적 접근이 이루어지고 있다. 그 중에 가장 주목할 만한 방법은 바로 flash 페이지와 함께 메인 html 컨텐츠(primary HTML)를 사용하는 방법이다. 이것은 flash를 알지 못하는 검색엔진이나 올바른 환경을 가지지 못한 사용자의 접근성에 있어서도 효율적인 방법이다.
이를 위해서는 Flash embed source 로 많이 사용하고 있는 SWFObject 와 SWFAddress가 사용될 수 있다. SWFObject 는 Javascript 를 사용하여 적절한 flash player를 확인하고 DIV tag 의 내용을 flash content로 교체하여 임베드 할 수 있게 도와주는 오픈소스 코드로써 간단한 방법으로 flash 를 임베드할 수 있도록 많은 기능들을 제공하고 있어 플래시 개발자들에게 널리 알려져있다. 그리고 SWFAddress는 flash 사이트 상에서 deep linking 을 제공하는 스크립트로 각 링크마다 고유의 페이지 url을 가질수 있으며 따라서, 웹브라우저에서 Back,Forward 기능을 사용하여 플래시사이트를 제작할 수 있다.
아래코드는 프레임기반의 간단한 네비게이션 메뉴를 SEO를 고려하여 제작한 샘플이다.
<HTML sample code>
<script type="text/javascript" src="./js/swfobject.js"></script> <script type="text/javascript" src="./js/swfaddress.js"></script> </head> <body>
<!--primary content, for non Flash visitors--> <div id="flashcontent"> <p><a href="index.html#/menuBtn1">menu1 content</a></p> <p><a href="index.html#/menuBtn2">menu2 content</a></p> <p><a href="index.html#/menuBtn3">menu3 content</a></p> <p><a href="index.html#/menuBtn4">menu4 content</a></p> <p><a href="index.html#/menuBtn5">menu5 content</a></p> </div>
<script type="text/javascript"> var so= new SWFObject("seo_flash.swf","flash", "300", "50", "9", "#ffffff"); so.write("flashcontent"); </script> <noscript> This content requires a browser with JavaScript enabled. </noscript>
위코드에서 div 태그부분을 보자. swfobject 에서 div 태그는 해당 flash player 가 존재하지 않거나 ActiveX 가 활성화되지 않을경우 화면에 보여지는 내용이다. 알맞는 환경이 갖춰졌을 경우 div 태그안의 내용은 flash 파일로 대체되는 것이다. 플래시 컨텐츠가 보인다면 이 정보는 사용자에게 보여지지 않는다. 하지만 검색엔진에는 이 정보가 노출되는 것이다. div 태그 안에는 flash 네비게이션 각 메뉴의 링크와 텍스트 정보가 포함되어있다. 그리고 앵커를 이용한 swfaddress 코드의 사용으로 flash 내부의 각각의 메뉴에 독립적인 url 을 생성해 주었다. 따라서, div 태그를 통한 외부링크도 실제적으로 사용가능하다. 다시말해, 검색엔진의 인뎅싱 결과로 생성된 링크가 실제 swf 파일 각각의 메뉴에 일대일대응으로 링크가 가능하다는 의미이다.
<flash sample code>
function handleChange(event:SWFAddressEvent) :void{ var path:String = event.path; gotoAndStop('$' + path); SWFAddress.setTitle(path); }
function onClick(event:MouseEvent):void{ var target:MovieClip=event.currentTarget as MovieClip; SWFAddress.setValue("/"+target.name+"/"); }
for(var i:int=1 ; i<=5 ; i++){ var buttonClip:MovieClip=this.getChildByName("menuBtn"+i) as MovieClip; buttonClip.buttonMode=true; buttonClip.addEventListener(MouseEvent.CLICK,onClick); }
SWFAddress.addEventListener(SWFAddressEvent.CHANGE, handleChange);
위와 같은 방법으로 컨텐츠를 구성한다면 플래시 안의 컨텐츠 내용을 검색엔진에 노출할 수 있을 것이다. 하지만 위 예제는 플래시로 구현할 수 있는 가장 간단한 형태로 실제 사이트 제작시 위와같이 단일한 링크 구성으로 이루어지는 경우는 거의 없을 것이다. 대부분 사용자의 메뉴클릭사이 마다 무수히 많은 인터랙션이 존재하고 시간의 흐름에 의한 에니메이션이 존재한다. 따라서,독립적인 메뉴의 링크를 통해 접속하는 경우 기존의 해당사이트와 단절된 느낌을 지울수는 없을 것이다. 이는 플래시 개발자가 좀더 고민해 보아야 할 문제이다.
download sample files...
reference from article below -How to SEO Flash (http://www.jehochman.com/articles/seo-friendly-flash.shtml) -How Google crawls my site (http://www.google.com/support/webmasters/bin/topic.py?topic=8843&hl=en) -A modern approach to Flash SEO (http://blog.deconcept.com/2006/03/13/modern-approach-flash-seo/) -SWFObject (http://blog.deconcept.com/category/swfobject/) -SWFAddress (http://www.asual.com/swfaddress/)
|
|
| 이 글의 관련글(트랙백) 주소 :: http://kimkijeung.com/trackback/108 |
|
|
|
|
|
|
|
| 다국어(multi-language) 작업시 폰트임베딩 문제는 적어도 누구나 한번 쯤은 겪여 보았을 것이다. 임베드하게되면 너무나도 용량이 커져버리게 되는 아시아국가 문자, 너무나 생소하여 원하는 문자들이 제대로 임베드되었는지 알 수 없는 유럽이나 남미쪽의 라틴계열의 문자들...이 모든것이 웹개발을 하는 개발자들이 유의해야만 하는 사항일 것이다.
용량문제에 있어서는 특별히 디바이스 폰트를 사용하지 않고 임베드를 사용할 경우 어느정도의 용량증가는 필히 감수해야 할 부분이라서 이부분은 제외하고 대신 정확한 폰트 임베드에 대해서만 글을 적어보려고 한다.
제목에서 언급한 키릴문자(cyrillic)는 동유럽쪽에서 많이 사용하는 문자로 러시아나 불가리아,우크라이나와 같은 나라에서 사용하는 문자이다. (e.g. фваыафиывапывап) 일반적인 라틴문자 이외에 특수한(?) 문자의 대표라 할 수 있는 키릴문자의 임베드에 대해 살펴보겠다.
플래시에서 폰트를 임베드하는 방법은 대표적으로 2가지로 나눌 수 있다. 첫번째는 직접 스테이지에 Text tool 을 사용하여 Dynamic textfield 를 생성하여 임베드하는 방법과, 두번째는 라이브러리에 있는 font symbol(Library-New Font) 을 사용하는 경우이다.
'나는 키릴문자가 제대로 임베드 되는데 왜 안된다고 그러지?' 라고 반문하는 사람이 있을 것이다. 맞는 말이다. 하지만 임베드가 가능하다는 사람은 첫번째 방법으로 직접 임베드를 한 경우 일것이다.
만약 두번째 방법으로 폰트를 임베드하려고 하면 키릴문자는 임베드가 안되었다는 것을 알 수 있을 것이다.(각각의 방법으로 생성된 임베드파일 크기에서 차이가 있음) 이것은 font symbol 을 사용할 경우 폰트 임베딩 방식이 시스템의 코드페이지(codepage)에 따라 달라지게 때문에 발생하는 문제이다. 즉, 국가마다 사용하고 있는 OS의 언어설정에 따라 임베드되는 폰트범위에 차이가 있다는 말이다. 만약 키릴문자를 사용하고 있는 러시아어나 우크라이나어로 OS의 언어 코드페이지가 설정되어있다면 두번째 방법으로도 임베드가 가능할 것이다. 대신, 그쪽 환경에서는 반대로 한글이 임베드가 안될 것이다.
font symbol 을 사용하여 폰트를 임베드하면 OS의 국가코드페이지범위와 Basic Latin, Latin 1 의 범위(unicode standard)까지 임베드가 가능하다. 물론 로컬환경에 따라 다를것이다.(대부분의 코드페이지에서는 라틴문자를 포함한다) 위 범위는 기본 라틴계열의 영어권국가와 프랑스어,독일어,스페인어와 같은 문자를 모두 표현할 수 있다. 하지만 헝가리어 같은 경우 Latin Extended A 까지 포함해야 모두 표현이 가능하다. 따라서 두번째 방법으로 폰트를 적절하게 임베드하는 방식은 코드페이지를 강제로 바꾸는 방법밖에 없다.(제어판-->국가및 언어옵션-->고급-->유니코드를 지원하지 않는 프로그램용 언어-->재부팅) 하지만 이방법은 너무 번거롭다.
Adobe에서도 font symbol 을 이용한 다국어 작업시 폰트 임베드 문제는 로컬시스템 환경에 영향이있으므로 첫번째 방법으로 직접 텍스트 필드를 생성하여 임베드하는 것을 권장하고 있다.
위 모든사항을 고려할 때 가장 안정적으로 다국어용 폰트를 임베드 하려면, 첫번째 방법을 사용해야할 것이다. 또한 임베드하려는 폰트가 해당문자(glyph)가 포함되어있는지 확인해 봐야한다. 대표적인 유니코드 폰트인 Arial 이나 Times New Roman 그리고 폰트뒤에 키릴문자(cyrillic)의 약자인 CYR 또는 CR 과 같은 첨자가 붙는 폰트를 사용할 경우 해당폰트를 표현하는데 무리가 없을것이다.
as3.0 의 경우는 hasGlyphs 과 같은 해당문자의 유무여부를 판단해주는 메서드가 존재하므로 이를 활용하면 좀더 안정적인 폰트 임베드가 가능할 것이다.
근데 왜 actionscript 로 character 임베드범위를 제어 못하게 했을까?....아님 할수 없는것일까?
download sample files...
reference from articles below http://www.quasimondo.com/archives/000211.php http://www.actionscript.org/forums/showthread.php3?t=108546&highlight=Cyrillic http://www.unicode.org/
|
|
| 이 글의 관련글(트랙백) 주소 :: http://kimkijeung.com/trackback/104 |
|
|
|
|
|
|
|
| as3.0에서 여래개의 이미지파일을 외부에서 로드하려면 일반적으로 flash 에서 기본적으로 제공하는 Loader 클래스를 반복적으로 사용하여 처리를 해야하는데 . 이를 위해 다수의 파일을 순차적 또는 한꺼번에 로드할 수 있는 좀더 통합적인 리소스 관리가 가능한 시퀀스 로더를 제작해 보았다.
본 클래스는 ASAP를 사용하여 제작한 loader 클래스를 AS3.0 버전으로 마이그레이션(migration)한 것이다. ASAP는 AS2.0 버전으로는 몇개 안되는 flash framework 중에 하나이다. 현재 전체적으로 AS3.0 버전으로 마이그레이션 중인 것 같다. 개인적인 생각으로는 코드자체가 방대하여 사용하기엔 좀 부담스럽지만 각각의 클래스와 클래스간의 연결고리, 이벤트처리와 같은 복합적인 내용들을 참고할 수 있어 유용한 부분이 있을 것이다. (ASAP as3.0 버전 SVN http://asaplibrary.googlecode.com/svn/)
<sample code>
import com.dstrict.ub.utils.loader.*; import com.dstrict.ub.events.*;
var loader:LoaderSequencer = new LoaderSequencer(1); loader.addEventListener(LoaderEvent.START, onLoadStart); loader.addEventListener(LoaderEvent.ALL_LOADED, onAllLoadFinished); loader.addEventListener(LoaderEvent.COMPLETE, onLoadDone); loader.addEventListener(LoaderEvent.PROGRESS, onLoadProgress); loader.addEventListener(LoaderEvent.ERROR, onIOError);
//아래와 같이 load 메서드를 사용하여 다수의 파일을 로더에 등록할 수 있다. // loader.load(fileUrl,fileName) loader.load("image1.jpg","1"); loader.load("image2.jpg","2"); loader.load("image3.jpg","3"); loader.load("image4.jpg","4");
function onLoadStart(evt:LoaderEvent):void{ //LoaderEvent 이벤트 오브젝트 속성인 fileName 으로 load 메서드의 fileName과 동일 trace(evt.toString()+"-------->start : "+evt.fileName); }
function onLoadDone(evt:LoaderEvent):void{ this["container"+evt.fileName].addChild(evt.loader.content); } function onAllLoadFinished(evt:LoaderEvent):void{ trace(evt.toString()+"-------->onAllLoadFinished"); } function onLoadProgress(evt:LoaderEvent):void{ trace(evt.toString()+"-------->progress : "+evt.bytesLoaded +" : "+evt.bytesTotal); } function onIOError(evt:LoaderEvent):void{ trace(evt.toString()+"-------->"+evt.message) }
view source code
download sample files...
|
|
| 이 글의 관련글(트랙백) 주소 :: http://kimkijeung.com/trackback/103 |
|
|
|
|
|
|
|
Convolution 는 회선기법으로써 입력픽셀과 그 주위의 이웃한 픽셀들을 가중평균(weighted average) 하여 처리하는 기법으로 플래시에서도 ConvolutionFilter 를 지원한다. 다른 필터에 비해 사용빈도에 있어서 떨어지지만 잘만 사용한다면 플래시에서도 포토샵에서 지원하는 여러 효과들의 필터 효과를 낼 수 있을것이다..
인접픽셀들을 이용하기 때문에 나타낼 수 있는 효과는 대부분 이미지의 경계부분(edge) 변형으로 생기는 embossing,blur,sharpen,accented-edge 같은 효과들이다.
convolution 필터는 9개의 파라미터가 존재하는데 일반적으로 아래와 같이 4개를 사용하여 제어를 한다.
ConvolutionFilter(matrixX:Number=0, matrixY:Number=0,matrix:Array=null,divisor:Number=1.0) 여기서 가장 중요한 부분이 matrix 라는 배열 파라미터인데 일반적으로 3x3 행렬을 사용한다. 0 0 0 0 1 0 => [ 0,0,0, 0,1,0, 0,0,0] 형렬표기방법은 좌측과 같이 열단위로 끊어서 작성한다. 0 0 0 <default> matrix 는 중앙에 있는 값(1)을 기준으로 대칭형태로 사용한다. 이는 인접픽셀과의 가중치 계산을 위한 비율과도 무관하지 않다. 또한 모든 matirx 의 요소 값의 합이 1을 기준으로 크게되면 전체적인 이미지의 밝기(brightness)가 증가하게 되고, 반대로 1보다 작게 되면 밝기가 감소하게 된다. 위에서 볼수 있듯이 matrix 값의 총합이 1이 아닐경우 원본값에서 이미지 밝기에서 차이가 난다. 이런 뜻하지 않은 이미지 밝기의 변화로 인해 의도했던 이미지 효과가 묻혀버리게 되는 일이 발생하는데 이런걸 방지하기 위해 divisor 를 사용하여 값을 조정해준다. divisor 은 convolution 필터의 4번째 파라미터로서 결과값에 대한 강도를 낮춰주게 된다. matrix 전체의 총합을 divisor 로 나눈값이 1을 기준으로 설정될수 있는 밝기로 생각하면 된다. ConvolutionFilter(3,3,[0, 0, 0, 0, 1, 0, 0, 0, 0]) ConvolutionFilter(3,3,[0, 0, 0, 0, 3, 0, 0, 0, 0],3) 따라서 위 두개의 필터값은 같은 효과를 나타낸다. <embossing>embossing 의 matrix 는 양수의 중앙값을 기준으로 좌우의 값의 부호가 반대인 좌우대칭형태 a d -c -2 -1 0 b e -b ==> -1 1 1 ==> new ConvolutionFilter(3,3, [-2, -1, 0, -1, 1, 1, 0, 1, 2]) c -d -a 0 1 2 <sharpening>sharpening 의 matrix 는 양수의 중앙값을 기준으로 상하대칭형태 a b c 0 -1 0 d e d ==> -1 5 -1 ==> new ConvolutionFilter(3,3, [0, -1, 0, -1, 5, -1, 0, -1, 0]) c b a 0 -1 0 <accented-edge>accented-edge 의 matrix 는 sharpening 과 비슷한 형태로 음수의 중앙값을 기준으로 상하대칭형태 a b c 0 1 0 d e d ==> 1 -3 1 ==> new ConvolutionFilter(3,3, [0, 1, 0, 1, -3, 1, 0, 1, 0]) c b a 0 1 0 위 모든 효과는 중앙값(e) 을 조정하여 효과의 강도를 조정할 수 있다. download sample files...
|
|
| 이 글의 관련글(트랙백) 주소 :: http://kimkijeung.com/trackback/102 |
|
|
|
|
|
|
|
| 우리나라의 웹환경에서는 초고속 인터넷과 같은 인프라의 발전으로 대역폭(bandwidth)을 고려해야할 만큼 네트워크 속도가 절대적으로 중요하지는 않다. 워낙 전송 속도가 빨라서 왠만한 웹사이트는 로딩시간없이 실시간으로 보여진다. 하지만 이는 특수한 우리나라의 경우에서만 해당하는 사항일뿐 아직까지 대부분의 다른나라에서의 전송속도는 생각하는 것 만큼 빠르지 않다. 좀더 범용적인 사이트의 개발을 위해서는 반드시 고려해야 할 사항이다.
특히 영상위주의 컨텐츠가 포함된 사이트의 경우 사용자의 대역폭을 고려하는 것이 좀더 사용자에게 좋은 컨텐츠를 제공하는 방법이 될 수 있다. 대부분 영상을 보여주는 방법으로 점진적 다운로드 방식(progressive download)을 사용한다. 실시간으로 다운로드 한만큼 플레이하는방식으로 이는 사용자 환경의 대역폭이 서비스의 질을 크게 좌우하게 된다.
영상을 보여주는 데 있어 점진적 다운로드 방식은 영상의 전체적인 용량보다는 영상의 압축률에 더 큰 영향을 받는다. 다시말해 데이타를 로드하면서 플레이하는 방식이기 때문에 비디오의 압축률을 높여 단위시간당 받을 수 있는 용량을 보다 크게 하여 좀더 원할하게 플레이 할 수 있다.
따라서 미리 사용자의 대역폭을 알아내어 거기에 알맞는 압축률의 비디오를 제공할 수 있는 것이다. 플래시에서 일반적으로 대역폭을 측정하는 방법으로는 일정크기의 샘플파일을 다운로드하여 걸린시간을 측정하여 구할 수 있다. 하지만 이 대역폭은 절대적인 속도를 의미하는 것은 아니다. 네트워크 사용량, 네트웍크 지연과 같은 여러가지 요인에 의해 언제든지 속도가 변경될 수 있다. 항상 측정된 속도로 데이타를 받을 수 있다는 의미가 아니다. 현재 시간에서 대략적인 사용자의 네트워크 속도를 가늠해 볼 수 있는데 의미가 있다. 불안정한 네트워크 일수록 측정 대역폭의 편차가 크게 발생한다.
대역폭 측정의 정확성을 높이기 위해서는 테스트하기 위해 사용하는 샘플의 용량을 크게 하거나,측정하는 테스트의 횟수를 늘려야한다. 하지만 이는 전송속도 측정을 위해 불필요하게 자원을 소모하게 되기 때문에 어느정도 적정선을 유지해야한다. 여기서 제공하는 방법은 샘플파일의 크기를 50~100 KB 로 제한하고 측정횟수를 2회로 한정하였다. 위 조건으로 측정한다고 해도 사용자의 네트워크 속도가 50KB 미만일 경우 100~200KB 를 테스트를위해 최소 2~4초정도의 시간이 걸리게 된다.
<bandwidthCheck class>
import com.dstrict.UB.events.Dispatcher; import com.dstrict.UB.events.Event;
class com.dstrict.UB.util.system.BandwidthCheck extends Dispatcher { private var _bandwidthSet:Array; private var _bandwidth:Number; private var _startTime:Number; private var _checkCount:Number; private var _loader:MovieClipLoader; private var _container:MovieClip; public function get bandwidth():Number{ return _bandwidth; } public function BandwidthCheck() { _checkCount=0; _bandwidth=0; _bandwidthSet=new Array(); } public function check():Void{ _container= _root.createEmptyMovieClip("container", _root.getNextHighestDepth()); var nocache:Number=Math.random()*1000000; _loader=new MovieClipLoader(); _loader.addListener(this); _loader.loadClip("bandwidthDummy.png?nocache="+nocache,_container); } private function calculateBandwidth():Void{ for(var i in _bandwidthSet){ _bandwidth+=_bandwidthSet[i]; } _bandwidth=_bandwidth/_checkCount; //dispatch a complete event startEvent(new Event(Event.COMPLETE,this)); } private function onLoadStart(targetMc:MovieClip):Void{ _startTime=getTimer(); } private function onLoadComplete(targetMc:MovieClip):Void{ _loader.unloadClip(_container); var elapsedTime:Number=(getTimer()-_startTime)/1000; _checkCount++; var progress:Object = _loader.getProgress(targetMc); var kilobytes:Number=progress.bytesTotal/1024; var kBps:Number=kilobytes/elapsedTime; _bandwidthSet.push(kBps); if(_checkCount==1){ check(); }else{ calculateBandwidth(); } } }
<sample code>
var bandwidthCheck:BandwidthCheck=new BandwidthCheck(); bandwidthCheck.addEventListener(Event.COMPLETE,onBandwidthCheck); bandwidthCheck.check();
function onBandwidthCheck(evt:Event){ trace("bandwidth----------------->"+evt.target.bandwidth); }
대역폭(bandwidth) 의 단위로는 KBps(kilobytes per second) 이다. 브라우저 캐쉬로 부터 다운받는것을 방지하기 위해 첨부한 문자열로 인해 반드시 웹상에서 다운받아야 로드할수 있다.
download sample files.......
|
|
| 이 글의 관련글(트랙백) 주소 :: http://kimkijeung.com/trackback/100 |
|
|
|
|
|
|
|
| flash에서 구현되는 대부분의 사용자와의 Interaction 은 버튼을 통해 이루어 진다. 즉, 버튼을 통해 이벤트가 발생되고 그것을 통해 필요한 반응들을 처리하게 된다. 일반적으로 flash 에 버튼을 설정할때는 아래와 같이 직접 이벤트 핸들러 함수를 등록하여 사용한다.
button.onRelease=function(){ //code....}
하지만 이런 방법은 무비클립이나 버튼 자체에 코드가 종속되어 버리게 되어, 제어해야할 버튼이 많을 경우 이곳저곳에 이벤트 핸들러 함수를 작성해야하므로 체계적인 이벤트 플로우를 관리하기 위해서 좀 부족한 감이 든다. 또한 버튼은 단독으로 한개가 있는것보다는 여러개의 버튼이 컨텐츠의 네비게이션으로서 작용하는 경우가 대부분이다. 따라서, 버튼을 릴리즈 했을 경우 활성/비활성되는 부분도 고려해야하는 문제가 있다.
여기에서 소개하는 방법은 Design pattern 중에 Template method 패턴을 활용하여 버튼 로직을 구현하는 부분을 구현하고 실제적인 기능을 담당하는 부분은 추상클래스의 상속을 통해 구현하는 방법을 사용하였다.
기본적인 개념은 추상클래스 타입을 확장하여 각각의 구상클래스로서 버튼클래스를 제작한다. 그리고 추상클래스 타입을 이용해 작성된 ButtonManager class 에 버튼 클래스를 등록한다. 여기서 ButtonManager class 는 버튼 등록, 활성/비활성 체크 같은 전체적인 버튼을 관리하는 역할을 담당하게 된다. (* 이곳에 사용된 startEvent 메소드는 기본적으로 제공하는 Dispatcher 클래스의 dispatchEvent 메소드를 재정의 한것이다. )
<AbstractSimpleButton Class> 버튼 추상 클래스로써 생성하는 모든 버튼에 공통적으로 상속되는 클래스이다. 이곳에 선언된 추상메소드를 구상클래스에서 오버라이드(override)하여 실제적인 기능을 구현한다.
import com.dstrict.UB.events.Dispatcher; import com.dstrict.UB.events.Delegate;
class com.dstrict.UB.ui.button.AbstractSimpleButton extends Dispatcher { private var _mc:MovieClip;
public function setController(inMc : MovieClip) : Void { _mc=inMc; } public function getController() : MovieClip { return _mc; } /**--------------------------------------------------------------------------- * Constructor function * @param inMc controller movieClip *---------------------------------------------------------------------------*/ public function AbstractSimpleButton(inMc:MovieClip) { setController(inMc); setButton(); } public function setButton():Void{ getController().onRollOver=Delegate.create(this,onRollOver); getController().onRollOut=Delegate.create(this,onRollOut); getController().onPress=Delegate.create(this,onPress); getController().onRelease=Delegate.create(this,onRelease); getController().onReleaseOutside=Delegate.create(this,onReleaseOutside); } public function onRollOver() : Void { // override in sub-class } public function onRollOut() : Void { // override in sub-class } public function onPress() : Void { // override in sub-class } public function onRelease() : Void { // override in sub-class } public function onReleaseOutside() : Void { // override in sub-class } public function enabled(mode:Boolean):Void { // override in sub-class } }
<ButtonManager Class> 추상클래스인 AbstractSimpleButton 클래스 타입을 이용하여 버튼의 등록및 이벤트 위임을 처리하고 있다. 어떠한 구상클래스(concrete class)도 사용되지 않았기 때문에 실제 구현과 완전히 분리되어 코드를 처리할 수 있다.
import com.dstrict.UB.ui.button.AbstractSimpleButton; import com.dstrict.UB.events.ButtonEvent; import com.dstrict.UB.events.Dispatcher; class com.dstrict.UB.ui.button.ButtonManager extends Dispatcher{ private var _buttonSet:Array; private var _selectButton:AbstractSimpleButton; /**--------------------------------------------------------------------------- * Constructor function *----------------------------------------------------------------------------*/ public function ButtonManager() { initialize(); } //================================================================== ///////////public method ////////////////////////////////////////////// //================================================================== public function addItem(item:AbstractSimpleButton):Void { _buttonSet.push(item); registerListener(item); killListener(item); } public function getItemAt(index:Number):AbstractSimpleButton{ return AbstractSimpleButton(_buttonSet[index]); } public function kill():Void { for(var i in _buttonSet){ AbstractSimpleButton(_buttonSet[i]).enabled(false); } } public function live():Void { for(var i in _buttonSet){ AbstractSimpleButton(_buttonSet[i]).enabled(true); } } //================================================================== ///////////event handler ////////////////////////////////////////////// //================================================================== public function onPress(evt:ButtonEvent):Void { startEvent(new ButtonEvent(ButtonEvent.ON_PRESS,evt.target,evt.currentMovieClip)); } public function onRelease(evt:ButtonEvent):Void { enabled(getSelectButton(),true); enabled(AbstractSimpleButton(evt.target),false); setSelectButton(AbstractSimpleButton(evt.target)); startEvent(new ButtonEvent(ButtonEvent.ON_RELEASE,evt.target,evt.currentMovieClip)); } public function onRollOver(evt:ButtonEvent):Void { startEvent(new ButtonEvent(ButtonEvent.ON_ROLL_OVER,evt.target,evt.currentMovieClip)); } public function onRollOut(evt:ButtonEvent):Void { startEvent(new ButtonEvent(ButtonEvent.ON_ROLL_OVER,evt.target,evt.currentMovieClip)); }
//================================================================== ///////////private method ////////////////////////////////////////////// //================================================================== private function initialize():Void { _buttonSet=new Array(); } private function setSelectButton(button:AbstractSimpleButton):Void { _selectButton=button; } private function getSelectButton():AbstractSimpleButton{ return _selectButton; } private function registerListener(item:AbstractSimpleButton):Void { item.addEventListener(ButtonEvent.ON_ROLL_OVER,this); item.addEventListener(ButtonEvent.ON_ROLL_OUT,this); item.addEventListener(ButtonEvent.ON_PRESS,this); item.addEventListener(ButtonEvent.ON_RELEASE,this); } private function killListener(item:AbstractSimpleButton):Void { var owner:ButtonManager=this; item.getController().onUnload=function(){ item.removeEventListener(ButtonEvent.ON_ROLL_OVER,this); item.removeEventListener(ButtonEvent.ON_ROLL_OUT,this); item.removeEventListener(ButtonEvent.ON_PRESS,this); item.removeEventListener(ButtonEvent.ON_RELEASE,this); }; } private function enabled(button:AbstractSimpleButton,mode:Boolean):Void { button.enabled(mode); }
}
event handler 함수를 보면 등록되어 있는 추상클래스 버튼에서 발생한 이벤트를 받아서 다시 최종적으로 ButtonManager 에 등록되어있는 이벤트 리스너 함수로 넘겨준다. 이 과정에서 필요한 버튼 이벤트 오브젝만 넘겨준다.
<ButtonEvent Class> 버튼에 대한 이벤트 정보를 담고 있는 이벤트 오브젝트.
class com.dstrict.UB.events.ButtonEvent extends com.dstrict.UB.events.Event {
public static var ON_ROLL_OVER:String = "onRollOver"; public static var ON_ROLL_OUT:String = "onRollOut"; public static var ON_PRESS:String = "onPress"; public static var ON_RELEASE:String = "onRelease"; public static var ON_RELEASE_OUTSIDE:String = "onReleaseOutside";
public var currentMovieClip:MovieClip; /**-------------------------------------------------------------------------- *constructor function *@param Type : String, 이벤트 핸들러 메서드 *---------------------------------------------------------------------------*/ public function ButtonEvent(Type : String,Source : Object,curMovieClip:MovieClip) { super(Type,Source); currentMovieClip=curMovieClip; } }
<SampleConcreteButton Class> 실제 버튼 기능을 구현하기 위한 구상 클래스. 단순히 버튼 롤오버 반응과 릴리즈시 활성체크를 구현하였다. 생성하려는 버튼의 기능에 따라 이 클래스는 다르게 구성된다. 물론 추상메소드로서 작용하는 AbstractSimpleButton 클래스의 onRollOver,onRollOut,onRelease,onReleaseOutside, enabled 는 오버라이드(override) 하여 구현하여야 한다.
import com.dstrict.UB.ui.button.AbstractSimpleButton; import com.dstrict.UB.events.ButtonEvent; class com.dstrict.UB.ui.button.SampleConcreteButton extends AbstractSimpleButton { public var buttonIndex:Number; public function SampleConcreteButton(inMc : MovieClip,inButtonIndex) { super(inMc); buttonIndex=inButtonIndex; } public function onRollOver() : Void { getController().gotoAndStop(2); startEvent(new ButtonEvent(ButtonEvent.ON_ROLL_OVER,this,getController())); }
public function onRollOut() : Void { getController().gotoAndStop(1); startEvent(new ButtonEvent(ButtonEvent.ON_ROLL_OUT,this,getController())); } public function onRelease() : Void { startEvent(new ButtonEvent(ButtonEvent.ON_RELEASE,this,getController())); }
public function onReleaseOutside() : Void { startEvent(new ButtonEvent(ButtonEvent.ON_RELEASE,this,getController())); } public function enabled(mode:Boolean):Void { getController().enabled=mode; if(mode){ getController().gotoAndStop(1); }else{ getController().gotoAndStop(2); } }
}
<sample code> 위에서 제작한 클래스를 활용하여 버튼 클래스를 생성하고 등록하는 코드 간단한 4개의 버튼을 구현하고 있다. 사용자는 SampleConcreteButton 를 만들어 버튼을 등록하기만 하면 모든 이벤트를 한꺼번에 관리할 수 있다.
import com.dstrict.UB.ui.button.ButtonManager; import com.dstrict.UB.ui.button.AbstractSimpleButton; import com.dstrict.UB.ui.button.SampleConcreteButton; import com.dstrict.UB.events.ButtonEvent;
var buttonMgr:ButtonManager=new ButtonManager(); buttonMgr.addEventListener(ButtonEvent.ON_RELEASE,this);
buttonMgr.addItem(new SampleConcreteButton(number1_mc,1)); buttonMgr.addItem(new SampleConcreteButton(number2_mc,2)); buttonMgr.addItem(new SampleConcreteButton(number3_mc,3)); buttonMgr.addItem(new SampleConcreteButton(number4_mc,4));
//event handler function onRelease(evt:ButtonEvent):Void{ result_txt.text="onRelease event : "+evt.currentMovieClip+"----->"+evt.target.buttonIndex; }
//Release event var numberButton:AbstractSimpleButton=buttonMgr.getItemAt(0); numberButton.onRelease(new ButtonEvent(ButtonEvent.ON_RELEASE,numberButton));
위 코드에는 ButtonManager에 등록되어있는 리스너가 onRelease 이벤트 밖에 없기 때문에 릴리즈 이벤트만 최종적으로 처리한다. 다른 버튼 이벤트를 처리하고 싶으면 리스너에 이벤트를 추가 하여 이벤트 핸들러 함수만 구현하면 된다.
등록된 모든 이벤트는 ButtonManager 의 이벤트 핸들러 함수를 통해 전달된다. 따라서 버튼에 대한 이벤트 플로우를 한곳에 집중할 수 있으며 기존에 코드에 종속되어있는 버튼에 비해 유연성이 크게 향상된다.
sample code 에서 맨 마직막 부분의 release event 부분은 사용자가 마우스를 통해 버튼을 클릭해서 release event 를 발생시키는 것이 아니라 직접 임의로 발생시킬 수 있다.
download sample........
|
|
| 이 글의 관련글(트랙백) 주소 :: http://kimkijeung.com/trackback/98 |
|
|
|
|
|
|
|
| 가변적인 메소드의 parameter 갯수를 이용하려면 AS2.0 버전까지는 arguments 오브젝트를 사용하여 임의의 parameter 에 접근할 수 있었다. 하지만 AS3.0 에서는 컴파일러의 좀더 철저한 데이타 타입의 체크와 parameter 관리에 의해 기존의 방법을 쓸 수가 없게 되었다.
<AS2.0> function parameterTest():Void{ trace(arguments.length) //parameter 갯수를 알수 있음 }
parameterTest(1,2,3); result--> 3
<AS3.0> function parameterTest():void{ trace(arguments.length) }
parameterTest(1,2,3); result--> compiler error : Incorrect number of arguments.
AS3.0 에서는 메소드 정의시 parameter 갯수와 호출시 parameter 갯수가 일치해야만 한다. 그렇지 않으면 위와 같이 arguments 갯수에 오류가 있다고 에러를 발생시키게 된다. 이를 위해 ...(rest) paremeter 구문을 새롭게 제공하였는데 사용법은 단순히 ... 뒤에 사용자가 원하는 parameter array 이름을 써주면 된다.
function parameterTest(...param):void{ trace(param.length) // param 은 배열 타입이다. }
다른 parameter 와 같이 사용하려면 function parameterTest(str1:String,str2:String,...param):void{ //.....statements } 마지막에 ...(rest) parameter 를 써주면 된다.
주의할 점은 위 방법을 사용하면서 arguments 오브젝트를 동시에 사용할 수 없다는 점이다.
|
|
| 이 글의 관련글(트랙백) 주소 :: http://kimkijeung.com/trackback/95 |
|
|
|
|
|
|
|
| Template method 패턴은 디자인 패턴 중에서도 가장 기본이 되는 패턴중에 하나다. 템플릿이란 단어에서 유추할 수 있듯이 어떤 일정한 틀을 기본으로 클래스를 구성하는 방식이다. 추상클래스(Abstract class) 에 정의되어있는 Abstract method 를 기반으로 알고리즘을 구성하는 template method 를 만들어 동작시키는 것이다. 다시말해 알고리즘의 캡슐화(encapsulation) 라고 할수 있다.
추상클래스(Abstract class) 에서는 단지 알고리즘만 기술하는 역할만 한다. 실제 구현은 서브클래스 (Concrete class) 에서 기술한다. 따라서 Template method 를 이용하면 알고리즘의 구조는 유지하면서 서브클래스에서 추상메소드를 오버라이드하여 재정의 할 수 있다. 서브클래스에서 행동을 지정할 수 있게 하면서도 코드의 재사용이 가능한 특징으로 Template method 패턴은 framework 를 제작하는데 자주 등장한다.
플래시에서는 abstract 키워드가 지원되지 않지만 개념상 큰차이없이 패턴을 사용할 수 있다.
<Abstract class>
class AbstractButton { private var _mc:MovieClip; public function setController(inMc : MovieClip) : Void { _mc=inMc; } public function getController() : MovieClip { return _mc; } //Constructor function function AbstractButton(inMc:MovieClip){ setController(inMc); setButton(); } //Template method public function setButton():Void{ getController().onRollOver=Delegate.create(this,onRollOver); getController().onRollOut=Delegate.create(this,onRollOut); getController().onRelease=Delegate.create(this,onRelease); getController().onReleaseOutside=Delegate.create(this,onReleaseOutside); } public function onRollOver() : Void{ // override in sub-class throw new Error("Abstract method!!!!!"); }
public function onRollOut() : Void{ // override in sub-class throw new Error("Abstract method!!!!!"); }
public function onRelease() : Void{ // override in sub-class throw new Error("Abstract method!!!!!"); }
public function onReleaseOutside() : Void{ // override in sub-class throw new Error("Abstract method!!!!!"); } public function enabled(mode:Boolean):Void{ // override in sub-class throw new Error("Abstract method!!!!!"); } }
<Concrete class>
class ConcreteButton extends AbstractButton{ function ConcreteButton(inMc:MovieClip){ super(inMc); } public function onRollOver() : Void{ getController().gotoAndPlay("over"); }
public function onRollOut() : Void{ getController().gotoAndPlay("out"); }
public function onRelease() : Void{ getController().gotoAndPlay("release"); }
public function onReleaseOutside() : Void{ } public function enabled(mode:Boolean):Void{ getController().enabled=mode; } }
위 sample 은 버튼을 구성하는 간단한 클래스이다. 플래시에서 Interaction 을 구현할때 거의 대부분버튼을 구현하여 접근한다. 일반적으로 버튼 액션을 구현하려면 버튼 인스턴스나 무비클립 인스턴스를 통해 버튼 이벤트를 주어서 사용하지만 그럴경우 제어해야할 버튼이 많아지면 코드길이가 늘어날 뿐더러 코드 유연성이 떨어지게 된다.
실제 버튼을 구현하려면 추상클래스를 확장하여 추상메소드를 구현하기만 하면 된다. 추상클래스의 추상메소드의 정의는 개발자가 신중히 결정해서 정해야한다. 추상메소드가 너무 많게 되면 서브클래스에서 구현해야할 코드가 많아져 불편한점이 있을 수 있다. 반대로 너무 추상메소드 자체를 큰 덩어리로 나누게되면 유연성을 떨어뜨리게 된다.
|
| | |