Creative Wrong Answer

Flex +30


데이터를 표시 하다보면 어쩔수 없이 버튼등으로 처리 하지 못하고 Text나 Label 등을 사용해서 보여줘야 하는 경우가 생긴다.

Text 데이터를 보여 주면서 외부 링크의 경우 htmlText에 직접 링크를 걸어주면 되지만 플렉스 내부에서 처리 해야 하는 경우에는 좀 곤란해지기도 하는데 이럴때 TextEvent로 htmlText의 링크를 체크 할수 있다.

TextEvent.LINK 와 TextEvent.TEXT_INPUT 두개밖에 없고 매우 간단하다

TextEvent () 생성자
public function TextEvent(type:String, bubbles:Boolean = false, cancelable:Boolean = false, text:String = "")

언어 버전:  ActionScript 3.0
런타임 버전:  AIR 1.0, Flash Player 9

텍스트 이벤트에 대한 정보가 포함된 Event 객체를 만듭니다. Event 객체는 매개 변수로 이벤트 리스너에 전달됩니다.

매개 변수
type:String — 이벤트 유형입니다. 이벤트 리스너는 상속된 type 속성을 통해 이 정보에 액세스할 수 있습니다. 사용할 수 있는 값은TextEvent.LINK 및 TextEvent.TEXT_INPUT입니다.
 
bubbles:Boolean (default = false) — Event 객체가 이벤트 흐름의 버블링 단계에 참여하는지 여부를 결정합니다. 이벤트 리스너는 상속된 bubbles속성을 통해 이 정보에 액세스할 수 있습니다.
 
cancelable:Boolean (default = false) — Event 객체를 취소할 수 있는지 여부를 결정합니다. 이벤트 리스너는 상속된 cancelable 속성을 통해 이 정보에 액세스할 수 있습니다.
 
text:String (default = "") — 사용자가 입력한 한 자 이상의 텍스트 문자입니다. 이벤트 리스너는 text 속성을 통해 이 정보에 액세스할 수 있습니다.


예제를 보면 첫번째 Google 링크는 외부 링크로 열리고 네이버 링크는 Flex 내부에서 처리해서 Alert을 띄운다.

[어플리케이션 스크립트소스]

private var text:TextLink;

private function init():void
{
 text = new TextLink();
 
 text.htmlText = "Google = http://google.com
Naver = http://naver.com"; text.addEventListener(TextEvent.LINK, checkLink); addChild(text); text.x = 100; text.y = 80; } private function checkLink(e:TextEvent):void { Alert.show(e.text+" 링크를 클릭했어요"); }

TextLink.as

package
{
 import flash.text.StyleSheet;
 import mx.controls.Text;

 public class TextLink extends Text
 {
  public function TextLink()
  {
   super();
  }
  
  override protected function createChildren():void
  {
   super.createChildren();
   
   var style:StyleSheet = new StyleSheet();
            style.setStyle("a:link", {color:"#0000ff", textDecoration:"underline"});
            style.setStyle("a:hover", {color:"#00ff00", textDecoration:"none"});
            style.setStyle("a:active", {color:"#0000ff", textDecoration:"underline"});
            textField.styleSheet = style;
  }
 }
}



TextLInk.as는 링크 컬러를 설정하기 위해서 Text를 상속받아서 만든 컴포넌트이다. Label, Text, TextArea, TextInput 등등 TextField를 가지고 있는 놈은 어떤것을 상속받더라도 상관없다.

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

Comment 0


플렉스에서 팝업에 관해서 가장 많이 나오는 질문중 하나가 팝업을 띄울때 어플리케이션의 데이터를 보내고 싶다거나 팝업이 닫힐때 데이터를 받아서 특정 함수를 실행시키고 싶다거나 하는 것이다.

이전에 쓴글 2010/02/17 - [Flex] - 커스텀이벤트 (Custom Event) 만들기 / 사용하기 를 보면 어느정도 알수 있지만 정확하게 팝업을 위해서 쓴 글이 아니기 때문에 실제 사용에 힘들어 하는 사람들을 보게 된다.

그래서 간단한 예제로 구현을 해보고자 한다.




예제를 보면 보낼 데이터에 rinn.kr 이 들어있고 팝업 띄우기를 클릭하면 팝업이 뜨면서 보낸 데이터가 보여진다.
팝업에 보낼데이터 필드에 들어있는 값이 어플리케이션에도 반영된다.

팝업으로 데이터를 보낼때에는 팝업컴포넌트의 public 변수에 그냥 값을 넘겨주기만 하면 되고. 팝업에서 데이터를 받을때에는 이벤트에 실어서 날려주면 된다.

팝업은 팝업을 띄우는 객체의 child로 취급되기 때문에 팝업에서 발생하는 이벤트를 어플리케이션에서 받을 수 있다. 기본적인 이벤트 전파만 이해 하면 사실 간단한 문제다.

워낙 간단하니 소스에 대한 설명은 생략하고 샘플 파일을 첨부하는 걸로 시마이~


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

Comment +7

플렉스의 레이아웃 컨테이너는 여러가지 종류가 있지만 가장 많이 사용되는 것은 Canvas, Box (VBox, HBox) 일것이다.

기본적으로 Canvas 는 absolute 기반으로 즉 좌표를 기반으로 절대위치를 결정해서 아이템을 배치 하게 되고 Box 계열은 vertical 이나 horozontal 정렬 방식으로 다른 아이템들과의 위치를 판단해서 전체적인 배치를 하게 된다.

어느것이 좋냐는 아빠가 좋냐 엄마가 좋냐 정도의 문제가 될수 있으니 일단 배제 하고..
아래 그림과 같은 레이아웃을 만든다고 가정하고 간단하게 구조를 잡아보자..


클릭해서 보면 상단,하단의 위아래로 나눠진 구조에 아래쪽은 좌,우로 나눠진 형태이다.

일반적인 사이트들의 레이아웃이고 관리자화면을 꾸밀때나 게시판 형식을 만들때 자주 보게 되는 형식일 것이다. (나만 이렇게 만드는건 아니겠지 -ㅅ-;)

상단은 높이가 고정되어있고 하단은 나머지 높이를 차지 하게 된다.
하단에서는 우측은 넓이가 고정되어있고 좌측은 우측을뺀 나머지 넓이를 차지하게 된다.

이런 형태의 레이아웃을 만들기 위해서 일반적으로 생각을 할때 위아래로 배치 되어야 하니까 어플리케이션은 vertical로 레이아웃을 설정하고 상단/하단 구분을 위해서 HBox 를 두개 배치 하고 하단 HBox 내부에 다시 데이터 그리드와 패널을 배치 하는 형태가 될것이다.
만약 데이터 그리드 위쪽에 정보를 보여주거나 설명을 위한 라벨만 하나 들어갈려고 해도 하단 VBox를 추가해서 라벨과 데이터 그리드를 넣어야 한다. 몇개 안되는 구조를 위해서 컨테이너가 꽤나 많이 사용되는 것이다.

이런 방법은 예전에 HTML 페이지에서 테이블로 구조를 만들어본 경험이있는 사람들이 즐겨 쓰는 방법이다. 지금은 HTML도 div를 사용해서 적절하게 겹치지 않도록 레이아웃을 하지만.. 전에는 중첩 테이블을 이용해서 td 안에 테이블 또 td 안에 테이블 이런 형태로 레이아웃을 구성했었다.

그렇게 만들었던 경험이 저런식으로 레이아웃을 만들게 되는 것이다.

중첩 구조로 만들때의 문제점은 Flex3 컨테이너가 모든 이벤트 처리 및 스크롤 등을 전부 가지고 있는 놈이라는 거다. 이벤트는 컨테이너를 따라 계속 버블링 되고 스크롤을 사용하던 사용하지 않던 이미 내부적으로는 맹렬하게 계산해서 처리를 하고 있다.

Flex4 에서는 Group이 추가 되면서 저런 문제가 줄어들겠지만 지금 Flex3를 주력으로 사용하고 있는 입장에서는 아무래도 중첩 구조는 퍼포먼스나 이후의 구조변경을 할때에도 부담이 될 수 밖에 없다.

하지만 브라우저 사이즈에 따라서 유동적으로 변경되는 레이아웃을 좌표계를 기반으로 만들기에는 좀 불편하다.

이 포스트에서 소개 하고자 하는것은 제한 레이아웃 생성자인 constraintRowsconstraintColumns 이다.
absolute 타입의 레이아웃에서 미리 화면을 제한해서 분할 하고 그 제한된 위치를 참고해서 레이아웃을 구성할 수 있게 해준다.
constraintRows는 상하단으로 구분을 할때 사용하고 constraintColumns는 좌우측을 구분할때 사용한다.

<mx:constraintRows>
      <mx:ConstraintRow id="title" height="30"/>
     <mx:ConstraintRow id="content" height="100%"/>
</mx:constraintRows>

이 코드는 absolute 로 정의된 어플리케이션의 공간을 상단을 높이 30 으로 하단은 나머지를 사용하도록 미리 분할한다. 분할된 선은 보이지 않는다 -ㅅ-;

<mx:constraintColumns>
      <mx:ConstraintColumn id="list" width="100%"/>
      <mx:ConstraintColumn id="sideBar" width="200"/>
</mx:constraintColumns>

이 코드는 constraintColumns 를 사용하여 좌측을 200으로 우측은 나머지를 사용하도록 분할한다.


컨테이너를 사용하지 않고도 화면을 미리 분할 해놓고 가상의 선을 기준으로 좌표를 지정할 수 있게 되는 것이다.

상단 전체, 하단 전체, 좌측 전체, 우측 전체, 1,2,3,4 로 나눠진 부분공간 모두를 사용해서 원하는 곳에 아이템들을 배치 할 수 있게 된다.
또한 어플리케이션이 absolute 이므로 고정좌표를 사용해서 특정 위치에 특정 아이템을 배치 하는 것도 물론 가능하다.

분할된 가상공간의 좌표를 지정할때는 id:좌표 형식으로 지정하게 된다.
제한 좌표를 사용할때는 top, left, right, bottom 속성으로 레이아웃의 크기를 설정 해주는것이 사용하기 편하다.

예를 들어 3번 위치에 가득차게 데이터 그리드를 배치하고 오른쪽 공간과 5px 정도 떨어져서 간격을 유지 하게 하고싶다면.

<mx:DataGrid  top="content:5" left="list:5" bottom="content:5" right="list:5">

이렇게 설정한다. 상대좌표를 잡을 constraintRow나 column의 아이디를 쓰고 거기서 얼마나 떨어져야 할지를 입력한다.
위 아래는 Row를 참조하고 좌우는 Column을 참조 해서 좌표를 잡게 된다.
저런 좌표를 디자인 뷰에서 직접 입력하기는 힘들지만 완성된 레이아웃은 디자인 뷰에서도 정상적으로 보이게 되니 처음 레이아웃을 잡을때 큰 틀을 설정해놓고 내부 아이템을 세팅해 내가는 형식으로 구성하면 된다.

이 방식으로 만들게 되면 단순 레이아웃을 위한 컨테이너의 중첩을 사용하지 않아도 되기 때문에 코드를 보기에도 깔끔해지고 중첩으로 인한 부담되는 이벤트 버블링도 방지 할 수 있다.

레이아웃만을 위해서 중첩된 컨테이너를 사용하는 것은 지양하자.


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

Comment +4

  • 아도겐~!!!ㅋㅋ

  • 엥 웬 관리자의 승인입니꽈!?

    게다가 자바스크립트 오류있어요~

  • 가변형 컨테이너는 편리하지만 동시에 독이죠.
    항상 느낌이 과거 html에서 유행하던 표안에 표안에 표의 느낌으로 레이아웃을 잡아가는 분위기입니다.

    세계적으로 뭔가 그리드에 대한 꽁수가 아니라 혁신적인 컨테이너 레이아웃에 대한 개발이 필요한 시기인듯합니다.

이전 포스팅에서 환경 세팅을 위한 파일을 받고나서 벌써 시간이 꽤 흘러버렸다..
2010/02/09 - [Flex/BlazeDS / iBatis] - Flex와 BlazeDS, iBatis를 사용하기 위한 환경 설정하기 - 다운로드

환경 세팅은 됐다고 치고 -ㅅ-;
프로젝트를 만들어보자...

포스팅 타겟은 초보자인데.. 어째 좀 불성실한 느낌이 들지만 기본 톰켓환경 세팅등은 다른곳에 워낙 잘 정리되어있으니 구글님을 참고 해서 세팅 하는걸로 하고 넘어간다.

이클립스에 플렉스 플러그인으로 설치되어있고 톰켓 5.5에 BlazeDS.war 파일도 가지고 있으니 이클립스에 서버를 세팅하고 플렉스 프로젝트를 만드는 과정을 그림과 함께 친절하게 따라가보자.

1. 이클립스를 실행한다.
[그림 생략]...

2. 서버를 만들자.
기본 톰켓 서버를 사용하지 않고 이클립스에 물려서 사용한다. 간단하게 켜고 끌수 있고 상황도 바로 바로 파악할 수 있는데다가 로그도 이클립스에 찍힌다. 처음 이클립스를 사용할때 되게 신기해 했던 기억이...
이클립스를 끄면 서버도 꺼지고 버전별로 관리도 되고 암튼 편하다..


프로젝트에서 new - other 클릭


Server - Server 선택 Next


본인 pc에 깔려있는 버전 선택.. 저기 나온다고 해서 안깔려있는 서버가 돌아가는건 아니니 깔려있는걸로 선택.
회사 프로젝트 때문에 톰켓 5.5를 사용하고 있기 때문에 5.5 선택하고 Finish~


프로젝트 네비게이터에 보면 만들어진 서버가 나오게 된다. 간단한 세팅도 할 수 있지만 그런건 다른데서 보기로 하고 여기서는 일단 만들었으니 서버는 끝~

3. 실제 돌아갈 프로젝트를 만든다.

new Project 를 만들고 나오는 창을 살펴보자


어플리케이션 서버 타입을 "J2EE" 를 선택한다.


만들어놓은 서버와 같은 타입을 선택한다. 여기서는 톰켓 5.5

이전 포스트에서 받은 BlazeDS 파일의 경로를 입력한다. ContextRoot 가 aaa 로 되어있는데 저건 신경쓰지 말고 원래의 프로젝트 이름으로 나오도록 그냥 가만히 놔두자 aaa로 고쳐버리면 낭패.. 스샷용으로 만들고 있는거니 일단 고치지 말고 진행하자.


BugReport 라는 프로젝트를 생성했고 프로젝트의 구조는 위의 그림과 같다.
flex_src 는 플렉스쪽 프로젝트가 들어가야 할 폴더이고 src 는 자바 소스 파일이다. 웹컨텐츠는 빌드를 했을때 파일이 나갈 곳이고 톰켓의 루트 폴더가 된다.

4. 프로젝트를 서버에 추가 하자

 하단 퍼스펙티브에 위에서 서버를 추가 했으므로 서버탭이 보이고 현재 서버의 상태가 보일것이다.
설치된 서버를 오른클릭해서 팝업메뉴를 불러서 Add and Remove Projects 를 선택한다.

만든 프로젝트를 오른쪽으로 옮기면 현재 서버에 프로젝트가 추가된다. 이제 생성된 프로젝트를 서버환경으로 테스트가 가능하게 된것이다.
여러개의 프로젝트를 서버에 띄워놓고 테스트가 가능하다. 경로는 http://localhost:8080/프로젝트이름/ 형식이 된다.


프로젝트를 등록했으니 오른쪽에 있는 스타트 버튼을 클릭하면 톰켓 서버가 시작된다.

5. 시작해보자~

생성한 프로젝트의 메인 파일을 F11을 눌러서 실행하게 되면. 서버에서 돌릴꺼냐 플렉스 프로젝트에서 돌릴꺼냐물어보는게 나오는데 그냥 플렉스 프로젝트로 돌리는걸 선택하면 된다.
Run on Server 를 선택하면 당연히 안된다-ㅅ-;;;


이 경로로 실행이 된다면 성공~!!!!

페이지를 찾지 못한다는 404 에러가 나오면 서버가 켜져 있지 않거나 컴파일 경로가 달라서 일것이다.
혹은 서버를 시작하면서 에러가 났다거나.. 등등..

프로젝트 설정에서 Flex build Path 쪽을 살펴 보면서 뭐가 틀렸는지 확인 해보자~

모두 성공하길... -ㅅ-~

ps. BugReport 라는 프로젝트를 하나 완성 시키면서.. 아이템 랜더러와 아이템 에디터 프로젝트 레이아웃이나 이벤트 등 잡다한 걸 묶어서 한방에 다 정리 해버릴까.. 생각 중이긴 한데.. (액션스크립트 트레이닝 책처럼.)
회사 일 하면서 하는거라 언제 완성 될지는 잘 모르겠다 -ㅅ-;;;

뭐 언젠가는 되겠지.

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

Comment 0

오늘 좀 한가하다 -ㅅ-;;;
덕분에 하나 더 올리게 되었다.

이번 포스팅은 이전 포스팅의 계량형이라고 할까.. 업그레이드 버전 정도 된다.
2010/03/09 - [Flex/Event] - 데이터 그리드 ListEvent / DatagridEvent 특정 칼럼만 에디트 하기

목적

이전 포스팅에서 더블클릭으로 아이템 에디트 모드로 들어가고 클릭으로 선택 하는 형식으로 만들었는데 이번에는 itemRenderer를 이용해서 에디트 버튼을 가지고 있는 데이터 그리드를 만든다.
그리고 디비에 데이터를 업데이트 한다고 할때 어느 시점에서 업데이트를 해야 하는지 CollectionEvent를 이용해서 알아본다.

										rinn				퍼플린				B										magis				들캔디				A										zzatung				짝퉁				??									.btnEditColumn{			upSkin: Embed(source="assets/edit.png");			overSkin: Embed(source="assets/edit_over.png");			downSkin: Embed(source="assets/edit.png");		}																


기본적으로 mxml 부분은 이전과 같다.
Datagrid 에 붙어있던 각종 이벤트 처리 부분이 랜더러로 빠져나가기 때문에 Datagrid 태그쪽만 좀 한가해졌다.

아이템랜더러는 엄청나게 많이 사용되고 질문또한 많이 올라오는 부분중에 하나이다. 간단하게 버튼을 넣는거 부터 복잡하게는 랜더러와 에디터를 따로 커스터마이징 해서 사용하는 경우까지 상황이 다양하지만. 기본적으로는 비슷하기 때문에 이 포스트에서 다루는 정도만 해도 기초적인것은 커버 될꺼라 생각한다.

위 소스 에서 <mx:DataGridColumn dataField="name" itemRenderer="renAddEditButton"> 이곳에 설정된 itemRenderer 로 사용되고 있는 renAddEditButton 이 이번 포스트의 핵심이다.

일단 소스 풀소스를 보자.

package{	import flash.events.MouseEvent;		import mx.containers.Canvas;	import mx.controls.Button;	import mx.controls.DataGrid;	import mx.controls.Label;	import mx.controls.dataGridClasses.DataGridColumn;	import mx.controls.dataGridClasses.DataGridListData;	import mx.controls.listClasses.BaseListData;	import mx.controls.listClasses.IDropInListItemRenderer;	import mx.core.ClassFactory;	public class renAddEditButton extends Canvas implements IDropInListItemRenderer	{		private var _btnEdit:Button;		private var _label:Label;		private var _listData:DataGridListData;				public function renAddEditButton()		{			super();		}				override protected function createChildren():void		{			super.createChildren();			this.horizontalScrollPolicy = "off";						_label = new Label();			addChild(_label);			_btnEdit = new Button();			_btnEdit.styleName = "btnEditColumn";			_btnEdit.visible = false;			addChild(_btnEdit);						_btnEdit.addEventListener(MouseEvent.CLICK, hBtnClick);						this.addEventListener(MouseEvent.MOUSE_OVER, hRollOver);			this.addEventListener(MouseEvent.MOUSE_OUT, hRollOut);		}				override protected function updateDisplayList(unscaledWidth:Number, unscaledHeight:Number):void		{			super.updateDisplayList(unscaledWidth, unscaledHeight);			_label.width = unscaledWidth-21;			_label.x = 3;			_label.y = 2;			_btnEdit.x = unscaledWidth-18;		}				private function hBtnClick(e:MouseEvent):void		{			var dg:DataGrid = DataGrid(listData.owner);			dg.editable = true;			dg.editedItemPosition = {columnIndex:1, rowIndex:listData.rowIndex};			trace("aa");		}				private function hRollOver(e:MouseEvent):void		{			_btnEdit.visible = true;		}				private function hRollOut(e:MouseEvent):void		{			_btnEdit.visible = false;		}				public function set listData(value:BaseListData):void		{			_listData = DataGridListData(value);			_label.text = listData.label;		}				public function get listData():BaseListData		{			return _listData;		}			}}


복잡해보이지만 별건 없다.
데이터 그리드의 아이템 랜더러를 사용하기 위해서 IDropInListItemRenderer를 구현한다. 저 인터페이스가 요구하는건 데이터 그리드의 listData를 엑세스 하기 위한 getter/setter 뿐이다.

기본흐름

createChildren을 override 해서 버튼과 라벨을 만들어준다.
updateDisplayList 에서는 위치를 잡아주고
데이터 그리드에 오버 했을때 버튼이 나타나도록 하기 위해서 해당 이벤트 핸들러를 등록한다.
랜더러 안에 있는 버튼을 클릭했을때 해당 데이터 그리드의 참조를 획득해서 editable = true 로 세팅해주고 이전 소스에서 나왔던 Datagrid.editedItemPosition 을 사용해서 해당 셀을 에디트 가능 상태로 만든다.

DataGrid 에는 itemEditEnd 이벤트를 걸어주고 아이템이 수정이 끝났을때 editable = false 로 다시 세팅해준다.

메인 mxml 부분의 스크립트를 보자.

import mx.events.CollectionEvent;import mx.collections.ArrayCollection;import mx.events.ListEvent;[Bindable]private var acData:ArrayCollection;private function init():void{	acData = new ArrayCollection(csData.node);	acData.addEventListener(CollectionEvent.COLLECTION_CHANGE, hCollectionChange);}private function hCollectionChange(e:CollectionEvent):void{	if(e.kind == "update")	{		if(e.items[0].property == "name")		{			trace(e.items[0].newValue);		}	}}


dataProvider로 사용하는 ArrayCollection 에 CollectionEvent를 받을수 있는 이벤트핸들러를 등록한다.
CollectionEvent는 ArrayCollection이나 XMLListCollection 같은 ListCollectionView를 구현하고 있는 넘들의 데이터의 변화 상황을 알려준다.

Inheritance : CollectionEvent Inheritance Event Inheritance Object

The mx.events.CollectionEvent class represents an event that is dispatched when the associated collection changes.

CollectionEvent(type:String, bubbles:Boolean = false, cancelable:Boolean = false, kind:String = null, location:int = -1, oldLocation:int = -1, items:Array = null)

아이템이 수정이 완료 되고 데이터 프로바이더에 반영되고나면 CollectionEvent가 발생하게 되고 이걸 캐치 하게 되면 수정사항이 반영된 ArrayCollection에 접근할 수 있다.

CollectionEvent.COLLECTION_CHANGE 이벤트의 핸들러인 hCollectionChange 함수에서 이벤트의 kind 를 확인 해서 업데이트가 된것인지 삭제가 된것인지등의 컬렉션의 변화 사항을 체크 한다.

e.kind 의 값은 "add", "update", "remove" 가 있고 update 일 경우 items 값은 mx.events.PropertyChangeEvent 가 된다.
정석으로 하려면 e.items 를 PropertyChangeEvent로 캐스팅 하고 거기에서 kind 값을 다시 받아서 "update"인지를 확인 하고 해야겠지만.. 어차피 이 데이터 그리드에서는 변경이 라벨 변경밖에 되지 않으니.. 그냥 0 으로 받고 변경된 property가 name 필드 인지만 확인 하는 정도로 하자.

trace(e.items[0].newValue);


에서 새로 입력된 값이 나오게 된다. 이 시점에서 디비에 수정된 사항을 업데이트 하는 정도로 구현하면 될것이다.

CollectionEvent는 이 경우 처럼 라벨을 수정하는 정도에서는 간단하게 처리가 되지만.
드래그앤 드랍이라던지. Tree에서 이용하기 위해서는 경우의 수가 많아진다.

따라서 그때는 CollectionEvent를 사용하지 않고 따로 업데이트 되는 시점을 체크 할 수 있는 방법이 필요하다.
드랍될때 데이터를 세팅한다거나 수정 할때 기본 itemEditor를 사용하지 않고 custom itemEditor를 사용해서 에디터 내부에서 변경된 값을 넘겨준다거나 하는 방식으로 하는것이 더 효율적일 수 있다.

하지만 CollectionEvent는 실제 작업을 할때 이용하지 않더라도 데이터 이동을 디버깅할때 여간 편하게 쓰이기 때문에 한번씩 사용해 보는것이 좋을 것이다.



ps.
이걸 만들면서도 뭔가 더 깔쌈한 방법이 있을꺼같은데 저정도 밖에 안되나 하는 생각이 들었는데. 좀더 나은 구현방법이 있으신분은 그냥 가지 마시고 꼭 댓글이나 트랙백을 달아주셔서 안목을 넓혀주셨으면 합니다.
저작자 표시 비영리 동일 조건 변경 허락
신고

Comment 0

데이터 그리드는 RIA 에서 빈번히 사용되는 컨트롤중 하나 이다.
관련된 이벤트도 많고 중요도도 높은 편이다.

데이터 그리드에서 editable = true 로 세팅 해주면 해당 칼럼을 그리드 내부에서 편집할 수 있다.
이것은 itemEditor를 통해서 가능해진다.

이 포스트에서는 특정 칼럼만 편집할 수 있도록 하고 몇가지를 더 추가 해서 완성하는 것을 해본다.

일단 mxml 부분 (태그 앞부분이 소문자로 나오는건 syntaxHighliter 때문이니 신경쓰지 않아도 된다)


	
		
			
				rinn
				퍼플린
				B
			
			
				magis
				들캔디
				A
			
			
				zzatung
				짝퉁
				??
			
		
	
	
		
			
			
			
		
	


아이디와 이름 혈액형을 model 로 데이터를 세팅하고 데이터 그리드에 바인딩 한다.
데이터 그리드에 연결되어있는 이벤트는 아래쪽에서 구현 예정이다.

아이디와 혈액형은 수시로 바뀌는게 아니기 때문에 여기서는 이름 필드만 편집 가능할 수 있도록 세팅 해보자.

여기서 사용 되는 것이 데이터그리드의 editedItemPosition 이다.
editedItemPosition property  
editedItemPosition:Object

The column and row index of the item renderer for the data provider item being edited, if any.

This Object has two fields, columnIndex and rowIndex, the zero-based column and row indexes of the item. For example: {columnIndex:2, rowIndex:3}

Setting this property scrolls the item into view and dispatches the itemEditBegin event to open an item editor on the specified item renderer.

The default value is null.

This property can be used as the source for data binding. When this property is modified, it dispatches the itemFocusIn event.


columnIndex와 rowIndex를 필드로 받고 아이템 에디트가 일어날때 그 값으로 세팅하게 되어있다.

아이템 에디트가 발생하게 되면 itemEditBegin 이벤트가 시작되고 이때 특정 칼럼만을 선택할 수 있도록 만들어준다.

private function dgEditBegin(e:DataGridEvent):void
{
	dg.editedItemPosition = {columnIndex:1, rowIndex:e.rowIndex};
}

columnIndex 를 1 로 고정하고 rowIndex 는 이벤트가 발생한 rowIndex 를 세팅 해주었으니 다른 칼럼을 선택하더라도 이름쪽만 에디트 될 수 있도록 바뀌게 된다.

하지만 매번 클릭시에 발생하게 되므로 사용하기에는 그리 모양새가 좋지 않다. 클릭 했을때는 해당 아이템이 선택되도록 하고 더블 클릭 했을때 수정 할 수있도록 수정하는것이 최종 목표이다.

더블 클릭을 사용할때 유의 할점은 일반 클릭도 함께 발생한다는 점이다.
마우스 클릭이 발생하게 되면 이게 더블클릭인지를 기다리는게 아니고 일단 마우스 이벤트는 진행이 되고 인터벌 안에 한번더 클릭이 들어오면 더블클릭으로 판단 하기 때문에 두가지 이벤트를 구분할수 있는 flag 를 세팅하고 아이템 에디트가 시작될때 클릭인지 더블클릭인지 확인하는 로직을 만들면 된다.

private var _isDClick:Boolean = false;

private function dgItemClick(e:ListEvent):void
{
	_isDClick = false;
}

private function dgDoubleClick(e:MouseEvent):void
{
	_isDClick = true;
}

private function dgEditBegin(e:DataGridEvent):void
{
	if(_isDClick)
		dg.editedItemPosition = {columnIndex:1, rowIndex:e.rowIndex};
	else
		e.stopImmediatePropagation();
}


isDClick 이라는 flag 를 itemClick 시에 false 로 세팅하고 doubleClick 시에 true 로 세팅해준다.
itemEditBegin 에서 flag 값에 따라서 이벤트를 블록 시킬 것인지 에디트를 진행할 것인지 결정하게 된다.



참고 사항

에디트 자체는 더블클릭일때만 활성화가 되지만 에디트를 하기 위해 더블클릭 하게 되면 itemClick 함수가 한번 실행 되게 된다.
이것을 클릭일때는 itemClick의 내용만 실행하고 더블 클릭일때는 에디트만 실행되도록 하기 위해서는 그냥 더블 클릭을 판단하는 로직자체를 타이머를 이용해서 구현하고 일반클릭일때는 더블클릭인지 판단하는 타이머가 끝날때 특정 행동을 수행하도록 수정해야 한다.

이 경우 그냥 클릭만 할때 바로 실행되지 않고 더블클릭인지 확인 하기 위해서 딜레이가 생기기 때문에 즉각적인 반응이 나오지 않아서 프로그램자체가 문제가 있지 않느냐는 평가를 받을 수도 있으므로 딜레이타임의 세팅에 세심한 주의가 필요하다.

이부분의 구현은 트랙백을 부탁한다.. -ㅅ-;;; 결코 귀찮아서가 아니다..;;


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

Comment +3

  • 이 포스트도 도움이 많이 되었습니다. 구글 검색하다보니 자꾸 이쪽 블로그로 오게 되네요.ㅋ
    밥이라도 한끼 사드려야 할듯..하군요..ㅋㅋ

    • 감사합니다 ㅎㅎ
      유입경로는 다 구글이더군요.
      다른 검색사이트(대표적으로 네이버)들은 자체 디비 검색을 우선하기 때문에 검색자체가 안되요

  • 신입개발자 2013.10.12 02:30 신고

    도움받아서 저도 글을 올립니다.
    많은 시간이 지나서 보실지 모르겠습니다.
    클릭 / dbl클릭의 구현에서 주기능은 타이머를 사용하지 않아도 됩니다.
    단지 그것을 위해서 사용하던 파라메터를 일정시간안에 초기화 해주는
    타이머 하나면 됩니다. 실제 컴퓨터에서 사용하는 더블클릭을 구현
    한다고 생각하면 편할듯 하고 이벤트는 ItemEditBegin을 이용하시면 일반 더블클릭과 동일한 결과물이 나옵니다.

어플리케이션에서 SWFLoader 를 사용하여 다른 도메인의 SWF 파일을 불러올때 나는 에러이다.

SecurityError: Error #2070: 보안 샌드박스 문제가 발생했습니다. 호출자 http://호출된domain/aaa.swf은(는) http://호출한domain/2008.swf이(가) 소유한 스테이지에 액세스할 수 없습니다.
    at flash.display::Stage/requireOwnerPermissions()
    at flash.display::Stage/addEventListener()
    at mx.flash::UIMovieClip/creationCompleteHandler()[E:\dev\flex\sdk\frameworks\projects\flash-integration\src\mx\flash\UIMovieClip.as:2432]
    at flash.events::EventDispatcher/dispatchEventFunction()
    at flash.events::EventDispatcher/dispatchEvent()


분명히 crossdomain.xml 도 확인 했고.. 재밌는건 에러를 무시 하고 나면 정상적으로 로딩이 된다. (이경우는 나만 그런건지도 모른다..)

독립적으로 돌아가는것을 확인 했고.. 스테이지를 왜 엑세스 할수 없다고 하는건지 전혀 감이 안잡혔는데.
구글링을 하던 도중 Embed 된 것들에 문제가 있는경우의 키워드를 획득!
css 부터 뒤지기 시작했다.

Embed 되는것은 어차피 컴파일 단계에서 swf 에 포함되는 것인데.. 왜 그것때문에 에러가 나는지 고민하던중 문제를 찾아냈다.

css 에서 이미지를 불러오면서 라이브러리 swc를 사용한 부분에서 에러가 났다.

보통 css 에서 이미지를 embed 시키는 경우 소스는.

.PanelTypeApp {

 borderSkin: Embed(source="../images/panelType01.png",
  scaleGridTop="171",scaleGridLeft="21",scaleGridRight="267",scaleGridBottom="180");
}


위와 같이 직접 소스에 이미지 경로를 넣게 된다.

플레시에서 스킨을 만들어서 export 해서 사용 하는경우는.

.PanelType1 {
	borderSkin: Embed(skinClass="Panel_borderSkin01");
}


이런식으로 swf의 클래스를 참조해서 가져오게 된다.
두 방식 다 Embed는 되지만 swc 같은 경우에는 라이브러리로 참조되어있어서 아마도 이 값을 다시 한번 찾는게 아닌가 싶다.
참조를 찾기 때문에 스테이지에 엑세스 할 수 없다고 에러가 나지만..
실제로는 Embed 되어있기 때문에 에러를 무시 하게 되면 이미지는 표시 되는 것이 아닌가 싶다.

이 에러는 오늘 처음 만난 에러라서 케이스가 이거 하나 뿐이라 정확한 이유는 아닐수도 있으니 비슷한 경우를 겪은 분이나 왜 저런식으로 문제가 해결 되는지 이유를 아시는 분은 댓글이나 트랙백을 남겨주셨으면 합니다.

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

Comment 0

TabBar / TabNavigator / ButtonBar 등.. ViewStack 과 관련된 컨테이너들을 사용하면서 가장 많이 나오는 질문은 "두번째 탭에 있는 오브젝트를 엑세스 하려고 하니 null 이라고 해요" 이다.

분명이 mxml 에서 다 만들어져 있는데.. 왜 안된다고 하는것인지 모르겠다고 질문이 올라오면.
가장 많이 달리는 답변이. "creationPolicy = "all" 로 해주세요" 이다.

과연 맞을까?

분명 저렇게 하면 에러가 나지 않고 전부 만들어진다.
creationPolicy = "all" 은 생성 정책을 전부 만드는걸로 하겠다는 뜻이다.
탭이 세개가 있으면 탭 세개 안에 들어있는 컴포넌트 들을 처음 로딩시에 전부 만들겠다고 선언하는 것이다. 그럴리야 없겠지만 백개가 있으면 백개를 다 만들게 된다.

공지 사항 탭 / Q&A 탭 / FAQ 탭  이 있다고 했을때 아에 첨부터 세개탭의 데이터를 전부 가져와서 뿌려놓고. 탭을 이동하면 단지 보기만 하면 되도록 만든다면.. 초기 로딩은 좀 느려지겠지만 이후에는 그냥 이동만 하면 되니 괜찮지 않느냐고 이야기 하는 사람도 있을 것이다.

복잡하지 않은 경우 이렇게 처리 하는 것도 뭐 나쁘지는 않을 수 있다.

하지만 뒤쪽 탭을 보지 않는 다면 쓸데 없이 시간과 메모리를 낭비 하게 되는 것 아닌가.

<mx:TabBar id="tabNav" dataProvider="{stack}"/>
<mx:ViewStack id="stack" width="100%" height="100%">
    <mx:Canvas label="Tab 1" backgroundColor="#ff0000">
        <mx:Label id="lbTab1" />
    </mx:Canvas>
    <mx:Canvas label="Tab 2" id="can2" backgroundColor="#00ff00">
        <mx:Label id="lbTab2" />
    </mx:Canvas>
    <mx:Canvas label="Tab 3" backgroundColor="#0000ff">
        <mx:Label id="lbTab3" />
    </mx:Canvas>
</mx:ViewStack>

위 코드에서 뷰스택은 캔버스로 되어있고 캔버스 안에 각각 라벨이 하나씩 있다.
이 라벨값이 1,2,3 이라고 입력되도록 해야 한다.

creationComplete 될때 lbLabel2.text = "2" 하게 되면 해당 객체가 null 이라서 엑세스 할 수 없다는 에러가 발생한다.
ViewStack 의 creationPolicy = "all" 해주면 에러가 깔끔하게 사라진다.

이 방법은 메인에서 탭으로 데이터를 주입해주는 방식이다.

이걸 Event 방식으로 바꾸려면 탭이 선택 되면 그때 자기의 데이터를 가져가도록 바꾸면 된다. 텝이 선택되서 바뀌고 나서 발생하는 이벤트가 이 포스트의 제목인 IndexChangeEvent 이다.
private function init():void
{
	stack.addEventListener(IndexChangedEvent.CHANGE, navIndexChange);
}

private function navIndexChange(e:IndexChangedEvent):void
{
	trace("old : "+e.oldIndex);
	trace("new : "+e.newIndex);
	
	switch(e.newIndex)
	{
		case 0 : lbTab1.text = "텝 1 입니다"; break;
		case 1 : lbTab2.text = "텝 2 입니다"; break;
		case 2 : lbTab3.text = "텝 3 입니다"; break;
	}
}

뷰스텍의 selectedIndex가 변경되게 되면 IndexChangeEvent 가 캐치 된다. e.newIndex / e.oldIndex 값을 통해 이전 인덱스와 바뀐 인덱스 값을 알 수 있게 되므로 해당 페이지에 맞는 내용을 로드 할 수 있게 된다.

간단한 페이지의 경우에는 creationPolicy = "all" 해버리는 쪽이 편하다고 생각할 수 있으나 탭안에 들어가는 내용이 많아 지게 되면 이런 방식으로 바꾸지 않으면 비효율의 극치를 보게 될지도 모른다.

플렉스에서 탭방식으로 동작 하는 것들은 전부 처음 로딩시에 보이는 페이지만을 가져오게 되어있다. 가벼운 RIA 페이지를 위해서는 기본은 지켜서 코딩을 하는 습관을 들이는 쪽이 좋다.

레퍼런스참고
http://livedocs.adobe.com/flex/3/langref/mx/events/IndexChangedEvent.html
저작자 표시 비영리 동일 조건 변경 허락
신고

Comment +2

  • 최근 플렉스 프로젝트를 처음 진행하면서 퍼플린님 블로그에서 많은 도움을 얻고 있습니다. :)
    트랙백하나 남기고 갑니다~

    http://cliver.egloos.com/1986719

    • 도움이 돼셨다니 다행입니다.
      요즘 업무에 치여서 블로그 글쓰는게 좀 뜸해졌는데 열심히 써봐야지요.
      트랙백 감사합니다~

mx.utils 에는 편하게 사용할 수 있는 api 가 담겨있다.

ObjectUtil.toString 같은 것은 많이 사용하고 꽤나 유용하게 쓰인다.

이 포스트에서 이야기 할 substitute 는 간단하게 이야기 하면 문자열을 치환 해주는 메서드이다.

간단히 코드를 보면 이해가 될것이다.

private var str:String = "select * from TABLE_NAME where userid='{0}' and menuid='{1}'";

private function build():void
{
	var query:String = StringUtil.substitute(str,"rinn","n002");
	trace(query);
	//select * from TABLE_NAME where userid='rinn' and menuid='n002'
}


위코드에서 보듯이 String 값에서 {n} 형식으로 중괄호로 묶여진 것들을 찾아서.
거기에 파라미터로 받은 값을 넣어준다.
n 값에 따라서 파라미터가 순서대로 들어가게 된다.

{0} 에는 첫번째가 {1}에는 두번째가 들어가고 문자열 안에서 등장하는 순서는 상관이 없다.

private var str2:String = "select * from TABLE_NAME where userid='{0}' and menuid=(select menuno from TABLE2 where userid='{0}' and menuid='{1}')";

private function build2():void
{
	var query:String = StringUtil.substitute(str2,"rinn","n002");
	trace(query);
	//select * from TABLE_NAME where userid='rinn' and menuid=(select menuno from TABLE2 where userid='rinn' and menuid='n002')
}


{0} 이 두번 나오면 두번다 파라미터 첫번째 값이 들어가게 되는 것이다.

이 방법의 좋은점은 대상이 되는 문자열을 파일로 저장해놓을 수 있다는 점이다.

기본 쿼리 같은 경우 파일로 저장해놓고 불러다가 치환해서 사용하게 되면.. 테이블 이름정도가 바뀐다거나 하는 것들은 간단하게 파일만 수정하는 것으로도 해결이 된다.

메뉴의 링크 정보를 저장하는 경우에도 편리하다. 권한 정보 같은것에 따라 파라미터가 바뀐다거나 하는 경우에.. 기본 링크 정보를 저장해놓으면 이후에 간단한 것은 컴파일 할 필요없이 수정해 줄수 있다.

사실 쿼리 같은 경우 iBatis 를 쓰는 것이 훨씬 간편하고 다이나믹 하게 쿼리를 생성할 수 있지만. 간단한 httpService 를 사용하는 경우에는 유용할 수 있겠다.

substitute 함수는 간단하게 구현되어있다.

public static function substitute(str:String, ... rest):String
{
    if (str == null) return '';
    
    // Replace all of the parameters in the msg string.
    var len:uint = rest.length;
    var args:Array;
    if (len == 1 && rest[0] is Array)
    {
        args = rest[0] as Array;
        len = args.length;
    }
    else
    {
        args = rest;
    }
    
    for (var i:int = 0; i < len; i++)
    {
        str = str.replace(new RegExp("\\{"+i+"\\}", "g"), args[i]);
    }

    return str;
}


간단하게 받아온 파라미터를 Array로 받고 for문 돌면서 정규식으로 찾아서 바꿔주는 구조이다.
사실 이런것은 필요한 경우에 그냥 만들어서 쓰게 되는데 이미 api 에서 제공하는 것을 귀찮게 만들어 쓸 필요가 있는가!!
그냥 있는 걸 쓰자.. -ㅅ-;

mx.utils 패키지 같은 경우에 간단 간단한 함수로 되어있기 때문에 api 소스를 보는 것 만으로도 초보 개발자에게는 공부가 된다. 결정적으로 그리 어렵지 않다..

시간날때 한번씩 뭐가 있는지 봐놓으면.. 나중에 뻔히 제공되는 메서드를 놔두고.. 개발하는 사태는 벌어지지 않을것이다..
우리는.. 그거 말고도 할일이 많지 않은가.. ㅠㅠ


아.. 주말도 출근 해야 하나.....

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

Comment 0

Flash / Flex 에서 사용되는 이벤트는 종류도 많고 거의 필요한 모든 이벤트가 있다고 해도 과언이 아니다.
그런데 왜 커스텀 이벤트가 필요할까.

기본이벤트에서 제공하는 target 속성으로 이벤트를 발생시킨 객체의 정보를 가져올 수 있다고 하지만.. 그 객체의 속성이 아니라 다른 변수를 넘겨야 할 필요가 있다거나 하는 경우..
기본 이벤트가 발생했을때 그 이벤트를 멈추고 새로운 이벤트로 발생시켜서 상위의 객체로 통보하고 싶다거나 하는 경우등.
커스텀 이벤트를 사용하게 되면 이것 저것 편리한 작업들을 많이 할 수 있게 된다.

이번 포스트에서는 커스텀 이벤트를 만드는 방법에 대해서 살펴보자.

커스텀 이벤트는 Event 를 확장해서 만들어진다.

package csEvent
{
	import flash.events.Event;

	public class CustomEvent extends Event
	{
		public static var SEND_DATA:String = "sendData";
		public var csData:*;
		
		public function CustomEvent(type:String, csData:*, bubbles:Boolean=false, cancelable:Boolean=false)
		{
			super(type, bubbles, cancelable);
			this.csData = csData;
		}
		
		override public function clone():Event
		{
			return new CustomEvent(type, csData, bubbles, cancelable);
		}
		
		override public function toString():String
		{
			var str:String = "[CustomEvent type="+type+" bubbles="+bubbles+" cancelable="+cancelable
							+" eventPhase="+eventPhase+" csData 변수를 사용해서 넘어온 데이터를 사용하세요]";
			return str;
		}
	}
}


CustomEvent.SEND_DATA 형식으로 사용할 수 있도록 하기 위해서 static 으로 변수를 설정해준다.
이 이름은 편한대로 지어도 된다. string 값이 기존의 이벤트와 겹치지만 않으면 된다.

csData 변수로 이벤트에 데이터를 실어서 보내게 되는데. 데이터 전송 전용 이벤트 이기 때문에 받는 타입은 그냥 와일드카드(*)로 받도록 했다.
특별히 id 같은 string 값만을 전송한다면 타입을 지정해주는 것이 좋다.

csData 같은 경우 위의 예제에서는 public 으로 선언되어있지만 캡슐화를 위해서는 private 으로 선언하고 getter 를 구현해서 readonly로 사용하는 것이 정확하다.
외부에서 직접 변수를 엑세스 하는 경우에 특별한 경우가 아니면 public 보다는 private 으로 선언하고 getter/setter 로 엑세스 할수 있는 통로를 만드는 습관을 기르는 것이 좋다. (하지만.. 이게 귀차니즘 때문에 쉽지가 않다.. ㅠㅠ)

clone() 함수와 toString() 함수는 옵션이다. 둘다 오버라이드 해서 구현하지 않더라도 정상적으로 이벤트가 전파된다. 하지만 역시 구현해주는 것이 좋다.

clone()을 오버라이드 해서 구현 해야 하는 이유를 아래처럼 설명하고 있다.
Why do you need to override clone? Well, the clone method creates a copy of your event (or object - whatever object has the clone event; this isn't limited to Event objects). The default clone method inherited by the Event class or whatever class your custom class extends, will return an event object of the type of that class, not your custom event subclass. In the situations where a clone is needed, it is needed to be of the same type of your class, not the class it extends.

-Flex PMD event Rule


toString() 같은 경우는 나중을 위해서나 다른 사람과의 협업을 위해서라도 해당 이벤트가 하는 일이나 넘어오는 변수의 타입등이 지정되어있다면 그 데이터를 보여주도록 만들어놓는 편이 좋다.
커스텀 컴포넌트를 만들때에는 항상 toString 을 구현해놓는 습관을 들이는 것이 좋을것이다.

dispatchEvent(new CustomEvent(CustomEvent.SEND_DATA, "http://rinn.kr", true));


이벤트를 디스패치 할때 두번째 옵션인 csData 파라미터에 원하는 데이터를 실어서 보내면 된다.
이벤트 리스너에서 CustomEvent 가 캐치 되면 아래의 함수에서 처럼 데이터를 확인 할수 있다.

import csEvent.CustomEvent;

private function init():void
{
	this.addEventListener(CustomEvent.SEND_DATA, csEventHandler);
}

private function csEventHandler(e:CustomEvent):void
{
	trace(e.toString());
	trace(e.csData);
}

// trace ----------------
// [CustomEvent type=sendData bubbles=true cancelable=false eventPhase=3 csData 변수를 사용해서 넘어온 데이터를 사용하세요]
// http://rinn.kr


e.csData 형식으로 넘어온 데이터를 확인 할수 있다. 어떤 형식도 가능하고 여러개 보내는 것도 가능하다.

커스텀 이벤트를 만들어서 팝업으로 뜬 오브젝트에 데이터를 넘겨주고 닫힐때 변경된 내용을 메인 어플리케이션쪽으로 보내주고 하는 등의 작업이 가능하다.
이렇게 이벤트를 만들어서 사용하게 되면 컴포넌트를 만들어서 사용할때에 parent.publicFunction 형식으로 직접 접속해서 뭔가 하지 않아도 되기 때문에 독립적인 컴포넌트를 만들 수 있다.

보통은 컴포넌트를 만들때 해당 컴포넌트의 이벤트를 정리해서 한꺼번에 만드는 경우가 많다.

as3 에서 이벤트가 중요한 개념이듯이 이벤트를 본격적으로 사용하기 위해서는 커스텀 이벤트가 중요한 개념이라 하겠다.
이벤트를 잘 사용해서 프로젝트를 효율적으로 끝내보자!!
저작자 표시 비영리 동일 조건 변경 허락
신고

Comment +4

  • clone() 메소드의 경우, 이벤트 버블링이나, 타 컴포넌트에서 받은 이벤트를 릴레이 해줄 때 꼭 필요했던 것으로 기억하고 있어요.
    오래되서 자세히는 기억이 안 나지만, clone()을 제대로 구현하지 않으면 에러가 나는 상황이 있었던 것 같네요. 가물가물..

    • 처음에는 clone()을 구현을 해서 사용했었는데.
      완료되지 않은 작업중에 clone() 없이 그냥 테스트 하다가.. 동작에 이상이 없어서 그냥 놔둬버린 케이스가 있었지요.

      일단 수정은 해놔야 할것 같군요.. 아 습관이 중요한듯.. 귀차니즘을 이길수 있는건 좋은 습관뿐인거같아요..
      귀찮다 생각할 겨를도 없이 코딩이 자동으로 되버리는.. 그런 습관을 기르고 싶군요 ㅎㅎ

  • 뭐가 더 좋을 지는 저도 잘 모르겠어요.
    깨진 창문은 물론 피하는 것이 맞지만, 모든 창문을 항상 반짝이게 닦아놓는 것은, 때로는 도움이되고, 때로는 그렇지 않기도 하니까요. :')
    늘 상황에 맞춰서 그때 그때.. ㅎㅎ 다만, 미리 알아두는 것은 도움이 되겠지요. ㅎㅎ

    • 심오하군요..
      깨진 창문 이론.. 그건가요 ㅎㅎ
      사용하지 않더라도 일단은 알아두는것이 좋다는거에는 백프로 동감이에요~


이벤트는 캡쳐-타겟-버블의 흐름을 탄다고 이전 포스트 (2010/02/10 - [Flex/Event] - Flex 이벤트의 전파단계와 버블링 ) 에서 이야기 했었다.

이번에는 이전 포스트의 마지막에 예를 들었던 특정 Event Phase 에서만 이벤트를 받는 것과 이벤트의 흐름을 끊는 것에 대해서 알아보자.



언제나 그렇듯이 백문이 불여일견 이라고 동작하는걸 보는쪽이 이해가 확실히 빠르다.

캔버스 세개에 MouseEvent.CLICK 을 받을 수 있는 리스너가 붙어있고 이벤트가 들어오면 오른쪽 텍스트에어리어에 뿌려주도록 되어있다.

useCapture All 체크 박스는 이전 예제와 동일하다. useCapture가 true 일때 이벤트가 이벤트가 어떻게 들어오는지 확인 하는 용도로 사용 하면 된다.

redCanvasStopEvent가 체크되어있지 않은 상태로 클릭을 하게 되면.
redCanvas - blueCanvas - blackCanvas 순서로 이벤트가 전파 되는것을 알수 있다.
체크를 하고 redCanvas 클릭하게 되면.
redCanvas 에서만 이벤트를 받고 이후의 이벤트는 전파되지 않는다.

이것은 redCanvas 이벤트핸들러 함수에 event.stopPropagation() 함수가 실행되기 때문이다.
이 함수는 이벤트의 전파를 막는다. 따라서 이벤트가 버블링 되지 않고 빨간색 캔버스에서 끝나버리는 것이다.

redCanvasStopEvent 를 체크 해제 하고 '블루 캔버스가 타겟일때만' 이라고 되어있는 체크박스를 체크상태로 만든 후에 빨간색 캔버스를 클릭해보자.

이벤트가 redCanvas - blackCanvas 로 전파되는 것을 볼수 있다.
파란색 캔버스의 핸들러 함수에 e.eventPhase == 2 일때만 실행 하도록 조건이 붙어있기 때문이다.

파란색 캔버스를 클릭하게 되면 정상적으로 이벤트를 받았다는 메시지가 출력될 것이다.
이전 포스트에서 이야기 했듯이.. 어떤 방법으로 이벤트를 걸러내서 원하는 이벤트만 받아서 사용할 것인지는 그때 그때 프로젝트의 상황에 맞춰서 잘 골라야 한다.

stopPropagation과 stopImmediatePropagation의 차이

두가지 다 이벤트를 멈춘다는 점에서는 동일하다.
하지만 차이가 있는데 말로 해서는 뭔소린지 잘 모르겠으니 예제를 보자.

blueCanvas 에는 리스너가 하나 붙어있고 redCanvas 에는 리스너가 두개가 붙어있다.
redClick1 함수에는 라디오버튼의 선택 상황에 따라서 stopPropagation과 stopImmediatePropagation 을 할것인지에 대한 조건문이 있다.

private function addListener():void
{
	blueCanvas.addEventListener(MouseEvent.CLICK, blueClick);
	redCanvas.addEventListener(MouseEvent.CLICK, redClick1);
	redCanvas.addEventListener(MouseEvent.CLICK, redClick2);
}






빨간색 캔버스가 클릭 되면 이벤트는 red1 - red2 - blue 순서로 활성화되는 것을 볼 수 있다.
stopPropagation 을 선택하고 클릭 해보면 red1 - red2 까지는 활성화 되지만 blue로 이벤트가 전파 되지는 않는다.
stopImmediatePropagation 선택하는 경우에는 red1 에서 끝나버린다.

둘다 멈추는것은 동일 하지만 전자는 currentTarget의 이벤트까지는 유지 해주게 되고. 후자는 모든 이벤트 플로우를 중단하게 된다.
(ps. 둘의 차이를 말로 명확하게 설명할 수 있는 분이 있으면 꼭 댓글을 남겨주세요 -ㅅ-;;)

이것의 차이도 target 과 currentTarget 의 차이 처럼 미묘하게 버그를 유발 할수 있으니 신경 써서 사용해야 할것이다.

이벤트의 흐름제어는 복잡한 UI 를 만들거나 Drag & Drop 같은것을 구현할때에 빈번하게 쓰이게 된다.
한번쯤은 고민 해보고 사용하는 것이 좋을 것이다.
저작자 표시 비영리 동일 조건 변경 허락
신고

Comment +2


모든 이벤트는 typetarget 속성을 가지고 있다. 이것은 어떤 이벤트가 발생했는지 어떤 객체가 이벤트를 발생시켰는지에 대한 정보이기 때문에 꼭 필요하다.
EventDispatcher 가 이벤트를 Broadcase(전파) 하게 되면 Listener가 발생한 이벤트를 받게 된다.

이벤트는 두가지 형식으로 나눠진다.
스테이지에 보이는 객체가 발생시키는 이벤트와 보이지 않는 객체가 발생시키는 이벤트이다.

스테이지에 보이는 객체가 발생시키는 이벤트는 이벤트 흐름(event flow)에 따라서 이동하게 된다.
여기서 나오는게 버블링이다.
보이지 않는 객체가 발생시키는 이벤트는 이벤트 흐름을 타지 않고 해당 객체에 직접 등록된 listener 에서만 캐치 할수 있다.

Loader 같은 것이 이런 경우이다.

private function loadImage():void
{
	var loader:Loader = new Loader();
	loader.load( new URLRequest("http://localhost:8080/logo.gif") );
	loader.addEventListener(Event.COMPLETE, loadComplete);
}
위 소스에서 처럼 loader에 직접 listener를 붙이는 경우에만 loader 객체가 발생시키는 이벤트를 받을 수 있다.

보이는 객체가 발생시키는 이벤트는 이벤트 흐름을 탄다고 했는데. 이제 본격적으로 알아보자.

1. 어떤 이벤트가 발생한다. (마우스 클릭, 키보드 누르기, 등등)
2. 어플리케이션은 어떤객체가 이벤트를 발생시켰는지 찾기 위해서 하위 자식들을 검색해간다. (캡쳐)
3. 어플리케이션이 이벤트를 발생시킨 객체를 찾았다 (타겟)
4. 이벤트의 정보를 가지고 다시 어플리케이션으로 돌아온다. (버블)

대부분의 이벤트는 이 버블링 단계에서 listen 해서 사용하게 된다.
거품이 물 안에서 위로 올라오는 것처럼 이벤트를 발생시킨 객체에서 어플리케이션으로 올라오기 때문에 버블링이라고 하는게 아닐까 싶다.
거품이 위로 올라올때 중간에서 가로챌수 있는것처럼. 이벤트가 발생하게 되면 발생시킨 객체의 상위 parent 에 listener가 등록되어있다면 그 이벤트를 받아서 처리 할 수 있다.

아래의 화면은 이벤트가 발생하는 것을 보여준다.
빨간색과 파란색에는 각각 MouseEvent.CLICK 리스너가 붙어있다.





파란색 캔버스를 클릭하게 되면 파란색 캔버스가 클릭되었다는 메시지가 나온다.
빨간색 캔버스를 클릭하게 되면 빨간색 캔버스가 클릭되었다는 메시지가 나온 이후에 파란색이 클릭되었다는 메시지가 나오게 된다.

이벤트 흐름

빨간색 캔버스가 MouseEvent.CLICK 이벤트를 디스패치 한다.
캡쳐 단계 - 어플리케이션은 빨간색 캔버스를 찾아 들어간다 ( Application - blueCanvas - redCanvas )
타겟 단계 - 빨간색 캔버스를 찾았고 빨간색의 리스너에게 클릭되었다는것을 알려준다 (redCanvas Click)
버블 단계 - 이벤트를 가지고 어플리케이션으로 올라온다. (redCanvas - blueCanvas - Application)
                이때 blueCanvas 도 Click 이벤트 리스너가 붙어있기 때문에 리스너에 등록된 함수가 실행된다.
                (blueCanvas Click)

빨간색 캔버스를 클릭한 후에 나오는 메시지의 Phase 를 보면 Red Click - phase : 2 라고 되어있다.
phase가 2 이기 때문에 타겟 단계에서 이벤트를 받았다는 이야기이다. 이때 event.target 으로 빨간색 캔버스가 참조된다.
Blue Click - phase : 3 에서 phase가 3 이므로 버블 단계임을 알수 있다. event.target 은 여전히 빨간색 캔버스이다.

여기서 target 과 currentTarget 의 관계도 알 수 있다.

event.target 은 이벤트를 발생시킨 객체이다. 빨간 캔버스를 클릭한 후 캐치된 두개의 이벤트 모두 target 은 빨간 캔버스이다.
event.currentTarget 은 리스너가 붙어있는 객체이다.
RedClick 이벤트의 currentTarget 은 빨간색 캔버스 이지만 BlueClick 이벤트의 currentTarget 은 파란색 캔버스가 되는 것이다.

이 관계를 잘 고려해서 이벤트 모델을 구성해야 자잘한 에러를 막을 수 있다.

예를 들면 canvas 를 base로 커스텀 컴포넌트를 만들었는데 안에 라벨이 들어있는경우 event.target 으로 캔버스를 받고 싶었지만 라벨쪽이 클릭된 경우 target 으로 label 이 넘어와서 오류가 난다거나 하는 경우가 생기게 된다.
target 과 currentTarget 을 구분해서 사용하는 것은 쉬운듯 하면서도 빠뜨리기 쉬운 부분이다.

위의 예제에서 useCapture 를 체크 한 상태로 캔버스를 클릭 해보자.

useCapture Flag 는 capture 단계에서 이벤트를 사용할것인가에 관한 것이다.

파란색 캔버스를 클릭하게 되면 캡쳐 단계에서만 이벤트를 받게 되므로.. 아무런 이벤트도 발생하지 않는다. 파란색 캔버스가 클릭되었다는 메시지가 나와야 할것 같지만. 파란색 캔버스의 경우는 target 단계이므로 아무런 이벤트도 받을 수 없는 것이다.

빨간색 캔버스를 클릭하게 되면 빨간색을 찾아가는 동안에 만나게 되는 파란색캔버스만 이벤트가 발생하게 된다.

이렇게 useCapture Flag 를 true 로 설정하게 되면 타겟과 버블링 단계가 무시되고 오직 캡쳐 단계에서만 이벤트를 받게 된다.
자주 쓰이는 속성은 아니지만 꼭 필요하게 되는 경우가 생기기도 하기 때문에 알아두는 것이 좋다.

늦은 감이 있지만 이쯤에서 addEventListener 를 한번 보고 가야 할것 같다.


addEventListener(type:String, listener:Function, useCapture:Boolean = false, priority:int = 0, useWeakReference:Boolean = false):void
리스너에서 이벤트 알림을 받을 수 있도록 EventDispatcher 객체에 이벤트 리스너 객체를 등록합니다.

네번째 파라미터인 priority 는 우선순위에 관한 것이다 숫자가 높을수록 먼저 처리되고 숫자가 같은 경우 먼저 추가된 리스너 부터 처리 된다.
하지만 useCapture 보다 더 쓰이지 않는다.

마지막의 useWeakReference 가 있는데 이것은 이전 글 [2010/02/05 - [Flex/Event] - Flex Event 기본 설명 ] 에서 removeEventListener 를 명식적으로 사용하기 힘든 경우에 체크 한다고 이야기 했었다.

한번더 강조 하지만 addEventListener 는 가비지 컬렉터가 메모리 해제를 하지 못하는 가장 많은 경우이다.
꼭 removeEventListener 해주는 습관을 기르자.


위의 예제에서는 빨간색캔버스가 클릭되어도 파란색캔버스에 붙어있는 listener Function이 실행되게 되는데 파란색캔버스가 직접 클릭되었을때만 실행되도록 하기 위해서는 어떻게 해야 할까.

이럴때 사용하는것이 로그에 같이 찍히던 phase 이다.

파란색 캔버스의 리스너함수에 event.eventPhase가 2 일때만 실행되도록 함수를 구성하게 되면 파란색 캔버스가 직접 클릭되었을때만 함수의 내용을 실행하도록 만들수 있다.
예제 아래에 적어져 있듯이 2 이면 타겟 이기 때문에 파란색 캔버스가 직접 클릭되었다는것을 알수 있게 된다.
event.currentTarget 값이 buleCanvas 일때의 조건을 사용해도 결과는 동일하다

또는 빨간색캔버스의 리스너에서 e.stopImmediatePropagation() 이나 e.stopPropagation() 으로 끊어줘도 동일하게 결과가 나타난다.

하지만 지금의 예제는 이벤트를 받는곳이 두군데 이기 때문에 같은 결과가 나오는 것이지. 많약 어플리케이션에서도 이벤트를 리슨 하고 있다면. 다른 결과가 나오게 된다.

만약 어플리케이션에서 마우스 이벤트의 리스너가 대기 중이라면 파란캔버스에 phase 로 조건을 줘서 걸러냈을경우 어플리케이션에서는 이벤트를 받을 수 있다.
하지만 빨간색 캔버스에서 이벤트 흐름을 끊었을 경우 어플리케이션에서도 이벤트를 받을 수가 없다.
이 부분은 다음 포스트에서 확인 해보자.

지금까지 이벤트 흐름에 관해서 정리해 보았다.
Flex 에서 이벤트 흐름은 아무리 강조해도 지나치지 않는다. 많은 자료들을 보고 정리 하는 습관을 길러야 한다.
저작자 표시 비영리 동일 조건 변경 허락
신고

Comment 1


BlazeDS는 adobe 에서 상용어플리케이션인 LCDS(LiveCycle Data Service)의 몇몇 기능을 제외하고 오픈소스로 풀어놓은 놈이다.
자바와 Flex 간의 소켓통신을 AMF 프로토콜을 이용해서 하게 되고 데이터를 받을때 RemoteClass를 세팅해서 별다른 변환 없이 자바쪽의 Bean을 Flex의 VO로 매핑 해주는등 일단 한번 맛을 들이고 나면.. json과 httpService의 조합은 더이상 처다보기도 싫을 정도로 편리하다.

BlazeDS 첫번째 포스팅이니 사용할 수 있도록 다운받아서 설치 해보자

일단 환경은 톰켓 5.5 / jdk 1.5 이상 / 이클립스 가니메데 / Flex sdk 3.4 이상 / 플레시 플레이어 10 을 기준으로 한다.
jdk는 1.5 이상에서만 BlazeDS를 사용할 수 있으니 주의하자.
 
1. JDK 다운로드

http://java.sun.com/javase/downloads/index.jsp
이곳에서 jdk 1.6을 받을 수 있다.
설치 할때 그냥 C:/java 에 설치하는 것을 추천한다. program files 하위로 경로가 잡히는데. 이게 은근히 귀찮다. 본인은 아파치도 톰켓도 전부 C 루트에 설치한다. 심지어 이클립스도 -ㅅ-;

2. 톰켓 다운로드

http://tomcat.apache.org/ 
5.5 든 6.0 이든 맘에 드는걸 써도 별 상관은 없겠다.

환경 변수등의 설정이 필요하면 검색 ㄱㄱ
이 포스트의 목적은 BlazeDS를 사용하는것이 목적이다!

3. 이클립스 가니메데 다운로드

http://www.eclipse.org/downloads/packages/release/ganymede/sr2
Java EE 버전으로 다운 받으면 된다.
2010년 2월 9일 기준으로 최신 버전의 이클립스는 갈릴레오sr2 버전이다. 플렉스 플러그인이 가니메데까지 밖에 안깔리는걸로 알고 있기 때문에.. 가니메데로 설치한다.
압축만 풀어주고 경로 설정만 해주면 끝.

혹시나 에러가 나면 eclipse.ini 파일의 jre 경로를 위에서 설치한 경로로 맞춰준다.

4. 플렉스빌더 이클립스 플러그인 다운로드

http://www.adobe.com/cfusion/entitlement/index.cfm?e=flexbuilder3
페이지 아래쪽으로 플러그인 다운로드 링크가 있다 60일 트라이얼 버전이고 Flex 시리얼을 동일하게 사용해서 등록할 수 있다.

5. Flex SDK 다운로드

http://www.adobe.com/cfusion/entitlement/index.cfm?e=flex3sdk
현재 3.5 버전이 릴리즈 되어있다.
다운 받아서 플렉스 빌더 플러그인의 sdk 경로에 함께 넣어서 관리하던지
따로 sdk 폴더를 만들어서 이전버전과 함께 관리한다. (추천)

6. BlazeDS 다운로드

http://opensource.adobe.com/wiki/display/blazeds/Release+Builds
BlazeDS turnkey 를 다운 받아서 사용한다.
압축을 풀면 docs/resources 등.. 폴더와 war 파일들이 있는데. 그중에서 blazeds.war 파일을 사용한다. 
sample.war 파일에 기본 샘플 프로젝트들이 있으니 참고 한다.

7. iBatis 다운로드

http://ibatis.apache.org/java.cgi
Beta 3 버전이 있지만 안정화 버전인 2.3.4 를 다운받아서 사용한다.
iBatis는 자바의 Beans 파일을 이용해서 데이터베이스의 필드와 매핑해서 결과를 넘겨준다.

Flex VO - Java beans - iBatis resultClass 까지 한방에 연결시켜서 데이터를 가져올 수 있다.

프레임웍을 따로 사용하지 않더라도
iBatis - 모델 , Flex mxml - 뷰, BlaseDS - 컨트롤러 정도로 MVC 형태로 구성할 수 있다.
더 확실하게 하기 위해서는 Spring framework 정도를 사용해서 MVC 프레임웍을 구성하는 쪽이 더 낫겠지만. 작은 프로젝트에서는 이정도로도 충분 하것이다.
스프링의 경우 BlaseDS와 연계된 프로젝트도 있으니 참고하기 바란다.
(Spring BlazeDS Integration Reference Guide)

이제 준비는 끝났다. 꽤나 많이 다운로드를 받게 되었는데.
다음 포스트에서 실제로 프로젝트를 만들어서 동작할 수 있도록 환경 세팅을 해보자.
저작자 표시 비영리 동일 조건 변경 허락
신고

Comment 0



Flex 에서 마우스 오른쪽 버튼을 누르면 나오는 컨텍스트 메뉴이다.
이놈은 플레시 플레이어가 기본으로 탑재 하고 있고 메뉴 자체를 삭제 할수가 없다.

따라서 플랙스에서 마우스 오른쪽 버튼을 눌렀을때 뭔가 행동을 해주고 싶다면 기본 컨텍스트 메뉴에 새로운 메뉴를 추가 해서 작업을 할수밖에 없는데 요걸 만들어 보도록 하자.

간단하기 때문에 한방에 소스 코드 가고 결과 화면 보고 중요한거만 설명하도록 한다.

new ContextMenu() 로 새로운 메뉴를 생성하고 new ContextMenuItem('text Alert') 으로 메뉴에 들어갈 아이템을 만든다.

이후에 customItems 라는 배열 property에 만든 menuItem을 push 해주면 보이게 된다.

this.container.contextMenu = menu;

를 통해서 원하는 컨테이너에서만 해당 메뉴가 보이도록 만들 수 있다.

이벤트는 두가지이다.

컨텍스트 메뉴 자체가 열리는 것을 확인 하는 ContextMenuEvent.MENU_SELECT 와 해당 메뉴가 눌렸음을 감지 하는 ContextMenuEvent.MENU_ITEM_SELECT가 있다.

메뉴가 열렸을때 해당 메뉴의 활성화 비활성화를 구별해줄수 있고.
메뉴 아이템이 선택되었을때 할일을 해주면 된다.



텍스트 필드와 버튼을 싸고 있는 컨테이너에 커스텀 메뉴를 추가 했기 때문에 바깥에서는 나타나지 않는다.



컨테이너에 클릭했지만 textField의 selectedText 값이 없기 때문에 역시 활성화 되지 않는다.



모든 조건이 맞을때 textAlert 이라는 메뉴를 사용할 수 있게 된다.

이하 소스와 실제 SWF 파일...

import mx.controls.Alert;
import mx.core.UITextField;

private function init():void
{
	var menu:ContextMenu = new ContextMenu();
	menu.addEventListener(ContextMenuEvent.MENU_SELECT, selectContextMenu);
	var menuItem:ContextMenuItem = new ContextMenuItem('text Alert');
	menuItem.addEventListener(ContextMenuEvent.MENU_ITEM_SELECT, selectContextMenuItem);
	
	menu.customItems.push(menuItem);
	menu.hideBuiltInItems();
	this.container.contextMenu = menu;
}

private function selectContextMenu(e:ContextMenuEvent):void
{
	trace(e.target);
	var tmp:ContextMenu = e.target as ContextMenu;
	if(e.mouseTarget is UITextField)
	{
		var tmpText:UITextField = e.mouseTarget as UITextField;
		if(tmpText.selectedText == "")
			tmp.customItems[0].enabled = false;
		else
			tmp.customItems[0].enabled = true;
	}
	else
	{
		tmp.customItems[0].enabled = false;
	}
}

private function selectContextMenuItem(e:ContextMenuEvent):void
{
	Alert.show((e.mouseTarget as UITextField).selectedText);
}



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

Comment 0

Flex Event 기본 설명

Flex2010.02.05 19:46
나는 디자이너 출신이기 때문에 프로그래밍에 대한 심오한 지식이 없다.
대학에서 윈도우 프로그래밍이나 자바, C 관련 언어에 대한 강의를 들었지만 디자인에 더 관심이 많았기 때문에 별로 주의깊에 듣지 않았었고... 그걸 지금 와서 엄청 후회하는 중이다.

Flash2 때 처음 Flash를 접하게 되었고 3 버전부터 사용을 하다가.. 4,5 를 넘어오면서 actionscript가 timeline 보다 점점 벽으로 다가왔지만.. 그때까지도 할만 했다.
하지만 2.0이 나오면서 부터 기존에 onClick 형식으로 타임라인에 스크립트를 입력하던 방식에 한계가 오고 내입장에서는 듣도보도 못한 addEventListener 라는놈이 나오기 시작하면서.. "아.. 개발자의 영역으로 가버리는구나" 생각하고 디자인에만 매진하게 되었다.

역시 이것도 지금은 후회하는 중이다.. 지금 개발자로 전향할줄 알았다면 그때 좀 힘들어도 포기하지 않았을텐데 -ㅅ-
지금도 이벤트가 정확하게 동작하기 위해서 trace로 확인하는 테스트 과정을 거쳐야 원하는 대로 결과가 나오는등.. 언제나 조금 복잡한것은 삽질의 연속이다.

역시 이 글도 [2010/02/02 - [as3] - Flex, Flash 가비지 컬랙션 방법과 메모리 관리] 요넘 처럼 정리 차원에서 쓰게 되었고. 글을 쓰면서 사용하게될 예제들도 사용하기위해서 테스트 하던 소스들이 될것이다.


AS3 는 이벤트를 기반으로 동작하도록 되어있어서 모든 행위들이 이벤트를 발생하게 된다.
이벤트를 이해 하고 있어야 복잡한 동작들을 원하는 시점에서 원하는 방식으로 정확하게 동작할 수 있도록 구현이 가능하다.

이벤트를 사용하면서 얻는 이점

이벤트를 사용하면 특정 개체에 종속적이지 않은 프로그램을 만들 수 있다.
이전의 프로그래밍은 특정 행위가 발생했을때 콜백함수를 호출하는 방식이었다.
이런 방식은 해당 컴포넌트가 상위의 컴포넌트에 종속되는 결과를 가져온다. 상위 컴포넌트에 정의되어있는 함수를 호출하게 되기 때문에 다른 컴포넌트에서 사용할수가 없게 되고 사용하기 위해서는 같은 이름의 콜백함수를 등록해야 한다.
이런 방식은 각 컴포넌트들간의 결합도를 강하게 만든다.

이벤트를 사용하게 되면.. 하위컴포넌트가 발생시킨 이벤트를 상위 컴포넌트가 받을수 있고 하위 컴포넌트 입장에서는 이벤트만 발생하면 땡이다.. 상위에서 받아서 쓰던지 말던지 신경쓰지 않아도 되는것이다.
누가 받는지.. 어디서 받아서 무슨일을 하는지 전혀 신경쓰지 않아도 되기 때문에 여러곳에서 해당이벤트를 받아서 사용할수도 있고 다른 컴포넌트에 추가 되더라도 어차피 이벤트만 발생시키는 것이기 때문에 사용하는데 전혀 무리가 없다.

공용으로 사용할 수 있는 독립적인 컴포넌트를 만들 수 있게 되는것이다.

이벤트는 비동기적이다.
따라서 어떤 작업을 실행시켰을때 화면이 멈추지 않고 다른 작업을 진행할 수 있다.
이벤트가 완료되고 실행되는 핸들러에서 완료되는 시점에 해야 할일을 정의 해놓으면 되는 것이다.
이 방법은 반대로 불편하게 느껴지기도 하는데.. 순차적으로 처리 해야 하는 일이나.. 모든것보다 우선해서 뭔가를 처리 해야 할때 기존의 방식대로 처리 하다가 에러를 만나게 되는 경우가 많다.
하지만 제대로 사용한다면 자유로운 세계를 만나게 될 것이다.

AS3 에서 이벤트는 EventDispatcher 라는놈이 관리 하게 되고 이벤트 디스패쳐를 상속해서 만들어진 컴포넌트 들은 이벤트를 발생시키거나 받을 수 있다.
플렉스의 기본이 되는 UIComponent 도 이벤트 디스패처를 상속하고 있기 때문에 모든 컴포넌트들이 이벤트를 주고받을 수 있는것이다.

개요는 이정도로 마무리 지어도 될것 같고..

이후에 포스트들에서는



등에 대해서 알아 볼것이다.

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

Comment 0

Q. 플렉스를 처음 배우려고 합니다. 책 추천좀....

이런 질문이 커뮤니티들 마다 잊어먹을만 하면 한번씩 올라옵니다.
생각난김에 정리해봤습니다.
제가 봤던 책들만 정리 했습니다.. 더 많은 책들이 시중에 있겠지만.. 다들 비슷비슷하기 때문에.. 몇권 본 이후로는 안산거 같군요.

목록에 없는 책중에서 추천해주고 싶은 책 있으면 댓글 남겨주세요.
사서 보고 리뷰에 추가하도록 하겠습니다
원서는 제외 했구요 한글로 정식 출판된 책들로만 구성했습니다.

개인적인 견해 이므로 책 선택에 참고만 하세요.

예제로 배우는 Adobe 플렉스(개정판)
카테고리 컴퓨터/IT
지은이 옥상훈 (에이콘출판, 2008년)
상세보기

플렉스에 아주 처음 입문 하는 분들에게 추천하는 책입니다.
기초 구성요소들에 대한 설명이 잘 되있고.. 제목 처럼 예제로 구성되어있어서 무난하게 볼수 있습니다.
http://okgosu.net 에서 피드백을 해주고 있고 세미나도 진행합니다.
입문이라면 이걸로 시작하는게 편할것 같습니다.


FLEX AIR BIBLE
카테고리 컴퓨터/IT
지은이 윤훈남 (대림, 2008년)
상세보기

객체지향 프로그래밍의 기본과 이벤트모델쪽을 관심있게 봤던 기억이 납니다.
메타데이터쪽도 많지는 않아도 정리가 되어있고. 모듈도 괜찮은 비중으로 다루고 있습니다.
한권쯤 가지고 있어도 괜찮은 책입니다.


FLEX 3 KNOWHOW BIBLE(FX)
카테고리 컴퓨터/IT
지은이 바닐라로이 (대림, 2008년)
상세보기

레퍼런스 번역 수준이라는 글이 있었지만 이것도 역시 볼만은 한 책입니다. 잘못된 정보도 조금 포함하고 있습니다. 아마도 외국 원문을 이해하는 과정에서 있었던 실수 있듯 합니다.
MDI 나 파일 업로드쪽을 다루고 있어서 급하게 찾아쓸때는 괜찮습니다. 디버깅에 페이지를 할당하고 있는것도 괜찮은 점중에 하나인것 같습니다.


ADOBE FLEX 3 실전 트레이닝 북
카테고리 컴퓨터/IT
지은이 제프 태퍼 (위키북스, 2008년)
상세보기

어도비마크를 달고 나온 책입니다.
쇼핑몰하나를 처음부터 만드는 과정을 통해서 플렉스의 기본과 기술을 설명하고 있습니다.
제가 책을 쓴다면 이런책을 써보고싶을 정도로 괜찮은 책입니다.
프로파일링 방법과 메모리쪽도 후반에 다루고 있습니다.


ACTIONSCRIPT 3.0 COOKBOOK
카테고리 컴퓨터/IT
지은이 조이 로트 (한빛미디어, 2008년)
상세보기

쿡북 특성상 간단한 팁같은 예제로 구성되어있습니다.
한번 읽어보고 옆에 두고 필요할때 찾아보면서 참고할만한 책입니다.
다루고 있는 내용도 쓸만한것들이 많습니다.


액션스크립트 3.0 디자인 패턴
카테고리 컴퓨터/IT
지은이 조이 로트 (에이콘출판, 2008년)
상세보기

디자인 패턴에 대한 기본적인 설명들과 AS3 에서 구현하는 방법을 다루고 있습니다.
입문자를 위한 책은 아니고 원래 다른 언어를 개발을 했었다거나 AS3 로 개발을 어느정도 해본 경험이 있다면 추천할만한 책입니다.
디자인 패턴은 몰라도 개발은 할수 있지만.. 알고 있으면 더욱 유지보수가 유용하고 남들에게 설명해주기도 좋은 어플리케이션을 만들 수 있습니다.


-----------------------------------------------------------------------------------------

제가 추천하는 책은

Adobe Flex3 실전 트레이닝 북 입니다.

입문자는 조금 어렵게 생각될지 모르지만 책에서 설명하는 코드들을 직접 타이핑 해가면서 한권을 끝낼 수 있다면 간단한 프로젝트 정도는 진행할 수 있지 않을까 생각됩니다.

옆에 끼고 급할때 쓰기 좋은 책은

ActionScript3 Cookbook 을 추천합니다.

가지고 있으면 간지나는 책은

역시 디자인 패턴 -ㅅ-;;;;  왠지 있어보인달까요..
물론 다 읽어야 겠지요~


실제 개발할때 가장 편리한 것은.. 위의 책들보다 구글신님과 라이브독 입니다.
검색하면 안나오는게 없어요..

즐거운 Flex Life 하시기 바랍니다.
저작자 표시 비영리 동일 조건 변경 허락
신고

Comment 0


이 글은 자꾸 까먹어서 정리겸 이해하고 있는 수준에 맞게 풀어서 쓴 글이다..
-------------------------

Flash나 Flex의 메모리는 개발자가 그냥 지울수가 없게 되어있다.
자바도 마찬가지고 VM기반의 언어에서는 메모리를 할당하고 해제 하는 과정이 시스템에서 알아서 하도록 되어있기 때문에.. 언제 메모리가 해제 되는지 개발자가 컨트롤 할수가 없다.

개발을 한 프로그램을 돌리기 시작했는데.. 메모리가 사용할수록 증가 한다면 그 프로그램은 결국에 가서는 시스템에 문제를 일으키고 종료될 것이다.

메모리 관리는 Garbage Collector (이하 GC) 라는 놈이 하게 되는데 말그대로 쓰레기를 수거하는 역할이다.
더이상 프로그램에서 사용하지 않는 객체들을 초기화하고 메모리를 시스템으로 반환해주게 된다.

플레시 플레이어는 객체가 생성되면 시스템에 메모리를 요청하게 되는데 이때 이후에 생길 객체들을 위해서 객체의 메모리보다 더 많은 양을 할당 받게 된다.
이 메모리가 부족해지면 다시 시스템에 더 많은 메모리를 요구 하게 되는데 이때 GC 가 일을 하게 된다. 쓸모 없는게 있는지 찾고 있으면 지우고 부족분에 대한 메모리를 요청하게 되는것이다.

개발자가 removeChild 나 null 을 선언할때 메모리가 해제되지 않는다는것이 중요하다.

GC 가 돌면서 사용하지 않는 것을 찾는 방법은 두가지 룰이 있다.

1. 레퍼런스 카운팅 (Reference Counting)
2. 마크 앤 스윕 (Mark and Sweep)


레퍼런스 카운팅은 해당 객체가 참조하고 있거나 해당 객체를 참조하고 있는 놈이 있는지 찾는다. 해당 객체를 참조하고 있는 애들을 찾아서 있으면 카운트를 1 올리는 방식이다.

전부 찾아보고 나서 카운트가 0 인 객체들을 메모리에서 해제한다.

함수에서 Label 을 만든다

var label:Label = new Label();


new 키워드로 인스턴스를 생성했으니 메모리 공간을 할당 받는다. 하지만 참조하고 있는 곳이 없다. 따라서 GC 가 실행되면 label 은 메모리에서 사라진다.

라벨을 만들고 어플리케이션에 추가한다.

var label:Label = new Label();this.addChild(label);


함수 안에서 만들어졌지만 어플리케이션에 추가 되었다.

어플리케이션은 child 로 label 을 참조하고 있고, label은 parent로 어플리케이션을 참조하게 된다.

this.removeChild(label);


하게 되면 참조 관계가 사라지고 레퍼런스 카운팅 값이 0 이 되어 사라질수 있게 된다.

어플리케이션안에 캔버스를 만들고 캔버스 안에 라벨을 만든다.

var canvas:Canvas = new Canvas();this.addChild(canvas);var label:Label = new Label();canvas.addChild(label);


어플리케이션이 child로 캔버스를 참조하고 있고 label은 parent 로 참조하고 있으니 캔버스의 레퍼런스 카운팅은 2 이다.
라벨은 캔버스와 관계가 있으니 카운팅은 1 이된다.

이때 캔버스를 삭제한다.

this.removeChild(canvas);


어플리케이션에서는 캔버스가 사라지고 아무것도 없다. 과연 캔버스 객체는 메모리에서 사라질까?
화면에서 사라졌지만.  캔버스와 라벨은 여전히 레퍼런스 카운팅 값이 1 이다. 상호참조 하고 있기 때문에 레퍼런스 카운팅 기법만으로는 여전히 삭제가 되지 않는다.

이러한 상호참조 문제를 해결하기 위해서 사용되는 방법이 두번째의 마크 앤 스윕이다.

마크 앤 스윕 기법은 어플리케이션에서부터 하위로 참조되고 있는 객체들을 찾아서 마크한다. 어플리케이션에서 부터 참조를 체크 한다는 것이 중요하다.
쭉 마크를 하고 모든 뎁스에 대해서 마크가 끝나면 마크가 없는 애들은 지운다.
마크 앤 스윕 방법은 레퍼런스 카운팅 보다 작업하는 시간이 더 걸리고 시스템에 부하도 많이 주기 때문에 자주 실행되지는 않는다고 한다.

위의 캔버스는 어플리케이션에서 참조되고 있지 않기 때문에 메모리를 반환하고 생을 마감하게 된다.

중요한 것은 위의 방법을 이해하고 사용하지 않는 것을 삭제 할때에 다음번 GC가 삭제 된것을 전부 쓸어갈수 있도록 코딩을 해야 메모리가 무한정 증가하다가 뻗는것을 막을수 있다.

그럼 메모리를 반환하는데 있어서 걸림돌이 되는것들이 무엇이 있을까.
가장 문제가 되는 것이 이벤트 리스너다.

객체지향으로 설계 하다보니 이벤트를 엄청나게 사용하게 되고 addEventListener가 컴포넌트를 만들다 보면 대여섯개씩 기본으로 붙는 경우가 허다하다.

이벤트는 기본적으로 이벤트를 디스패치한 객체의 참조를 가지고 날아가게 된다.
따라서 event.target 또는 currentTarget 으로 이벤트를 발생시킨 놈을 사용할 수 있는것이다.

어떤 컴포넌트가 이벤트를 리슨 하거나 디스패치 하게되면 그 객체는 레퍼런스 카운팅이 1이상으로 유지 되기 때문에 살아있는 이벤트가 하나라도 있으면 삭제가 되지 않는 문제가 발생한다.

따라서 addEventListener 해준것은 반드시 removeEventListener 해주는 습관을 들여야 한다.

지워지는 시점이 명확하지 않다거나 내부에서 참조되서 관리가 힘들다거나 하는 경우에는 언제 remove 해줘야 하는지 결정하기가 쉽지 않다.
이럴때 사용하는것이 useWeakReference 이다.

addEventListener (type:String, listener:Function, useCapture:Boolean = false, priority:int = 0, useWeakReference:Boolean = false):void
리스너에서 이벤트 알림을 받을 수 있도록 EventDispatcher 객체에 이벤트 리스너 객체를 등록합니다.

다섯번째 파라미터를 true 로 해주면 참조값을 약하게 잡고 있게 되고 이후에 GC 에서 삭제해줄 확률이 높아진다. 확률만 높아진다는 것이지 꼭 사라진다는건 아니기 때문에. removeEventListener 해주는 것이 가장 좋다.

이벤트쪽만 정리가 잘 되어있어도 메모리누수를 상당부분 막을수 있고.

플렉스IDE 를 사용한다면 개발중간중간에 프로파일러를 돌려서 체크 해보는 습관을 갖는게 좋을것같다.

나같은 경우는 커스텀 컴포넌트를 만들게 되면 destory() 함수를 만들어서 그 컴포넌트에서 사용되었던 이벤트 리스너를 일괄로 삭제하고 가능하면 컴포넌트 내부에 addChild 되어있던 것들도 삭제 해주는 함수를 만들어서 사용하고 있다.
removeChild 하기 전에 destory()를 실행시켜주고 삭제하면 기본적인 방지책은 되는것 같다.

PS. 메모리 관리에 좋은 방법들이 있으면 공유를 부탁드립니다~ 댓글 트랙백 환영
저작자 표시 비영리 동일 조건 변경 허락
신고

Comment +3

플렉스는 쓸만한 수준의 디버깅툴을 제공한다.
BreakPoint 를 사용해서도 작업을 할수 있고 Expression을 사용해서 특정 변수를 추적하는 것도 가능하다.

사용되는 함수를 전부 Step into 로 찾아들어갈 수도 있고 디버깅중에 BreakPoint를 추가할 수도 있다.

하지만 서버와 통신을 하는중에 발생하는 문제에 대해서는 정확하게 값이 넘어왔는데 플렉스쪽에서 처리를 잘못해서 데이터가 안나오는 것인지.
서버측의 문제인건지 명확하게 알기가 힘들다.


그래서 패킷을 감시하는 HttpWatch 라던지 서버쪽에 로그를 확인 한다던지 하게 되는데..
플렉스 콘솔에서 바로 확인할수 있는 방법도 있다.

1. <mx:TraceTarget /> 이라고 어플리케이션 아래에 적어준다..
2. 서버와 데이터통신을 시도한다..
3. IDE 하단의 View 탭의 Console 을 확인한다..


TraceTarget 을 적어놓기만 하면 http 통신이건 amf 소켓 통신이건 콘솔에 넘어온 데이터가 보이게 된다.


릴리즈 버전을 컴파일 할때는 주석처리 해주자..
저작자 표시 비영리 동일 조건 변경 허락
신고

Comment 0

SharedObject를 간단하게 이야기 하면 웹브라우저의 쿠키와 비슷하게 개발자가 원하는 데이터를 로컬에 저장하는 객체이다.

개별 유저에 커스터마이징된 화면이라던지, 이전에 방문했었는지의 여부 라던지.. 개발자가 필요에 의해서 데이터를 저장해야 하는 경우에 사용할 수 있다.

쿠키는 String 데이터만을 가지는데 반해 SharedObject 는 Array, Object 등 복합데이터를 저장할 수 있기 때문에 활용가능성이 무궁무진하다.
(단 메서드는 저장할 수 없다 -ㅅ-)

기본 용량은 100K 이고 저장할 공간의 크기를 늘이거나 줄일 수 있다. 저장하는 데이터의 크기가 정해져 있다면 굳이 크게 할 필요는 없는거니 상황에 맞춰서 적용하면 되겠다.

100K를 넘는 용량을 지정하게 되면 사용자에게 저장하는 것을 허용할 것인지 여부를 묻는 대화상자가 나오게 되는데 아니오 해버리면 사용하지 못하는 상황이 와버리니 주의해서 사용하면 된다.

기본적으로 도메인 기준으로 공유할수 있다. 쿠키처럼 다른 도메인에서 만들어진 SO는 읽을수 없고 같은 도메인에서 만들어진 SO파일만 읽을 수 있다.

만들어지는 파일은 *.sol 확장자를 가지게 되고.. 저장되는 경로는 운영체제 시스템에 따라 다르고 윈도우의 경우는
C:/Document and Setting/계정이름/Application Data/Macromedia/FlashPlayer 하위에 저장이 된다.

var so:SharedObject = SharedObject.getLocal("mySo")
형식으로 불러와서 쓸수 있다
기본적으로 Object 처럼 key value 형식으로 저장을 하게 되고 data 속성을 통해서 내부의 값들을 가져올 수 있다.

공유객체를 읽어오도록 하면 SharedObject객체가 존재 할때는 data 를 읽어오고 데이터가 없으면 새로운 객체를 생성하게 된다.

간단하게 예제를 보면서 확인 해보자.




SharedObject 데이터가 있다면 userName 과 description 에 값이 나온다.
없는경우 왼쪽에서 값을 입력하고 createSo 버튼을 누르면 생성된다.

오른쪽에서 key 텍스트 필드에 "userName" 이나 "description", "rinn" 이라고 입력하고 getSO 버튼을 누르면 아래의 필드에 값이 표시된다.

리프레시 해보면 왼쪽 필드가 채워져서 swf가 로딩될것이다.

스크립트 부분의 소스이다.

import mx.controls.Alert;
private var so:SharedObject;

private function init():void
{
	so = SharedObject.getLocal("mySo");
	if(so.data.userName == undefined)
		Alert.show("ShareObject가 정의되어있지 않습니다");
	else
	{
		txtUserName.text = so.data.userName;
		txtDescription.text = so.data.description;
	}
}

private function createSo():void
{
	setSo("userName",txtUserName.text);
	setSo("description", txtDescription.text);
	
	var ar:Array = [];
	ar.push("http://rinn.kr");
	ar.push("rinn@naver.com");
	setSo("rinn", ar);
}

private function clearSo():void
{
	so.clear();
	txtUserName.text = txtDescription.text = "";
}

private function setSo(key:String, data:*):void
{
	so.data[key] = data;
}

private function getSo(key:String):*
{
	if(so.data[key] == undefined)
	{
		Alert.show(key+" 는 정의되어있지 않습니다");
	}
	return so.data[key];
}

createSo 함수에서 보면 저장할 수 있는 데이터가 여러가지 형식이 가능하기 때문에 활용범위가 넓다.

기본적으로 페이지의 이동이나 브라우저가 닫힐때 저장을 하게 되는데 필요할때 즉시 저장하기 위해서는 so.flush() 를 사용하면 된다.
삭제하기 위해서는 so.clear()를 사용한다.

더 자세한 메서드는 레퍼런스 (http://livedocs.adobe.com/flex/3/langref/flash/net/SharedObject.html)를 참조하면 된다.

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

Comment 1

  • 참고

    SharedObject 생성이 안되면 so:SharedObject = SharedObject.getLocal("mySo", "/") 생성할때 뒤에 경로 "/"를 줘서 생성 해보세요~

대부분의 RIA 사이트는 느리다.
SWF 파일이 크고 왠지 플레시를 사용했으니 뭔가 가만히 있지 않고 움직여야 하지 않느냐 하는 요구 때문에 점점 파일은 커지고 로딩하는데 시간이 걸리게 된다.
HTML과 달리 일단 로딩하면 페이지 이동이 없다는 것으로 클라이언트를 설득하면서 넘어가게 되는데 RSL을 사용해서 다이어트를 해보자

RSL은 Runtime Shared Library의 약자이다.
말그대로 미리 라이브러리를 로드하고 그것을 사용하는것이 아니고 Runtime에 공유 라이브러리를 사용하겠다는 것인데.

이게 처음에는 좀 뜻이 모호하다는 생각이 들었다. 단편적으로 이미지나 컴포넌트 같은것을 따로 빼내놓고 그것을 가져다가 사용한다는 것으로 알고 있었던 것이다.

하지만 RSL은 프레임웍도 런타임에 로드 할수 있도록 해준다.

사용법은 간단하다.

플렉스프로젝트의 Property 에서 Flex Build Path 를 선택해서 살펴보면 상단에 콤보박스가 있다.

이것을 Merge into Code 에서 Runtime Shared Library(RSL)로 바꾼다.


이렇게만 해주면 일단 준비는 끝이다.. 바뀐것을 확인 하고 싶으면..

SDK의 framework.swc 를 살펴보자.


RSL URL 항목에 framework_3.4.0.9271.swz 라는 링크가 보인다.
이것이 공통프레임웍 부분이다.

Edit를 눌러서 살펴보면


Deployment paths에 두개의 파일이 등록되어있는것을 알수 있다.

이 두 파일은 실제로 하는 일이 같고 swz 확장자의 파일은 최신의 플레이어를 위한것이고 swf 는 swz를 지원하지 않는 플레이어를 위한 파일이다.

이렇게 생성된 파일은 웹브라우저가 아닌 플레이어에 캐시되고 이 파일은 Adobe에서 signed 된 파일이기 때문에 브라우저 캐시를 지워도 사라지지 않고 남게 된다.

다른 어플리케이션에서 RSL을 사용하는 경우 이 파일이 없으면 새로 다운을 받고 있으면 다운 받지 않고 있는 것을 사용하게 된다.

라이브러리 패스에 잡혀있는 다른 swc들.. ex) flex.swc 같은 경우도 RSL로 따로 뺄수 있고 라이브러리 프로젝트를 만들어서 커스텀 라이브러리 swc를 가지고 있는 경우에도 마찬가지로 RSL 로 사용할 수 있다.

중대형 프로젝트에서는 어플리케이션이 하나가 아니고 모듈 등으로 나눠져 있는 경우가 많기 때문에 RSL의 사용은 선택이 아닌 필수가 된다.

이때 약간 주의 해야 할 점이 있는데 이것은 아래의 용량 변화 표를 보고 이야기 해보자.

RSL 사용 전후 크기 변화 (단위 : KB)

   사용전  사용후
 index.swf  495  224
 serviceCreator.swf  702  283
 serviceManager.swf  349  97
 taskManager.swf  291  83
 serviceViewer.swf  637  138

공통 파일
framework_3.4.0.9271.swz   556
framework_3.4.0.9271.swf   553

위의 두개는 어플리케이션이고 아래의 세개는 모듈이다.
용량을 보면 거의 30% 정도로 크기가 줄어들었다. 
이때 주의 해야 할 점은 공통파일이 최소 한번 로딩되어야 한다는 것이다. 
일단 한번 로딩되고나면 클라이언트에 캐시로 잡히기 때문에 더이상은 신경쓰지 않아도 된다.

만약에 index.swf 만 사용한다면 로딩되어야 하는것은 495kb -> 224+556= 780k 로 오히려 늘어나게 된다. 
하지만 메뉴를 하나라도 불러서 사용하게 된다면 그때부터는 이득을 볼 수 있다. 

이 부분만 적용하고자 하는 프로젝트에서 확인하고 적용하면 될것이다.

아무리 인터넷 속도가 빨라졌다고 해도 RIA 사이트는 아무래도 다른 사이트보다 늦게 뜨게 된다. 기본 프레임웍을 RSL을 사용해서 로딩 부담을 줄이고 라이브러리 프로젝트를 사용해서 자주 사용되는 부분들만 따로 빼서 관리 하는것 만으로도 사용자의 부담을 훨씬 줄일수 있지 않을까 생각한다.

더 심도 있는 내용은
DieBuster  http://www.diebuster.com/?p=676  요쪽 히카님블로그에서 살펴보면 된다.
뭔가 레벨이 다른 분이라 오히려 적용보다는 참고만 하게 되는 블로그 -ㅅ-;

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

Comment 1

ExternalInterface 세번째 입니다.

ExternalInterface가 뭔지 전혀 모르겠다 하시는 분은
2009/12/30 - [Flex/ExternalInterface] - ExternalInterface 기본기
이 포스트를 먼저 읽어 보고 오시면 됩니다.

자바스크립트에는 유용한 기능들이 많습니다.
가장 많이 사용하는 window 객체에는 브라우저의 정보 및 페이지 정보를 알아올수 있는 메서드들이 포함되어있는데 페이지 정보는 location에 들어있습니다. 그중 몇가지를 살펴보면 아래와 같습니다.

window.location.href
주로 페이지 이동시에 많이 사용했던 속성인데 저 자체로는 현재 페이지의 정보를 가지고 있습니다.

아무사이트나 들어간 후에 주소창에 javascript:window.location.href 라고 쓰게 되면 현재 페이지 주소가 나타납니다.
자바스크립트는 클라이언트에서 직접 실행되는 스크립트이기 때문에 기본 메서드 같은 경우 이런식으로 브라우저 주소창에 쓰면 대부분 실행됩니다.

window.location.hostname
페이지의 호스트 주소 정보를 반환합니다.

window.location.pathname
호스트 네임 뒤로 실제 페이지까지의 경로를 반환합니다.

window.location.search
주소 부분에서 get방식으로 넘어온 파라미터 부분을 반환합니다.
이게 중요합니다.
플렉스에서 get방식의 파라미터를 받아올때에도 이것을 사용합니다.

간단하게 예제를 봅시당.

주소 예제 : http://rinn.kr/post/aa.html?blogID=rinn&name=퍼플린

window.location.href : http://rinn.kr/post/aa.html?blogID=rinn&name=퍼플린
window.location.hostname : rinn.kr
window.location.pathname : /post/aa.html
window.location.search : ?blogID=rinn&name=퍼플린

딱 보면 뭘 어떻게 사용해야 할지 감이 오실꺼라 생각됩니다.

HTML 페이지나 플렉스 어플리케이션에서 새창으로 다른 어플리케이션을 띄우면서 파라미터를 넘겨줘야 하는 경우가 생기는 상황이 많이 발생합니다.
내부에 로드 하는 경우는 여러가지 방법으로 데이터를 교환 할수 있지만 브라우저 자체가 달라지는 경우 에는 좀 힘들게 됩니다.

플렉스에서 넘어온 파라미터를 받는 Application.application.parameters 같은 경우 flashVar 로 넘어오는 것만 받을수 있습니다.
따라서 플렉스의 swf 를 임베드 할때에 파라미터를 같이 세팅 해줘야 하고 자바스크립트로 배열을 미리 만들어놓는다거나 해서 받아오도록 처리했었습니다.
데이터가 많아지거나 가변적이거나 할 경우 이런 방법은 꽤나 버그를 유발할수 있는 소지가 많습니다.

직접 주소를 가져다가 그냥 짤라서 사용하기만 하면 되니 이쪽이 더 쉬울꺼라 생각됩니다.

어플리케이션간에 파라미터 이동을 나만 고민 하지는 않았겠지요.
당연합니다 구글에 이미 사용이 가능한 소스도 있습니다


public static function getParameterValue(key:String):String
{ 
	var value:String;
	var uparam:String = ExternalInterface.call("window.location.search.toString");
	
	if(uparam==null)
	{
    	return null;
    }
    var paramArray:ArrayCollection = new ArrayCollection(uparam.split('&'));
    for(var x:int=0; x-1)
    	{
    		value = (p.replace((key + '='), '')).replace('?','');
    		x=paramArray.length;
    	}
    }
    
    return value;
}
깔끔하게 키값으로 받아올수 있도록 구현되어있습니다.


ExternalInterface.call("window.location.search.toString");

이렇게 직접 호출한 놈을 가지고 파싱해서 키에 맞는 value 값을 리턴해줍니다.

아래의 소스는 key-value 의 object로 넘어온 놈들을 전부 리턴 해주는 함수 입니다.


public static function getAllParameters():Object
{
	var _params:Object = {};
	var uparam:String = ExternalInterface.call("window.location.search.toString");
	
	if(uparam==null)
	{
    	return null;
    }
	
	uparam = uparam.replace('?','');
	
	var params:Array = uparam.split('&');
	var length:uint = params.length;
	
	for (var i:uint=0,index:int=-1; i<length; i++) 
	{
		var kvPair:String = params[i];
		if((index = kvPair.indexOf("=")) > 0)
		{
			var key:String = kvPair.substring(0,index);
			var value:String = kvPair.substring(index+1);
			_params[key] = value;
		}
	}
	return _params;
}

이처럼 자바스크립트를 사용할수 있는 Externalinterface는 활용할수 있는 부분이 꽤 많습니다.
이 글을 보시게 되는 분들도 좋은 방법이 있으면 저한테도 공유좀 부탁드립니다 -ㅅ-;;;




ps.
전체를 가져오는 소스와 키로 받아오는 소스는 제작자가 다른 소스 입니다.
북마크를 날려먹은 관계로 원본 블로그 주소를 찾을수가 없군요.
이곳저곳에서 퍼다가 링크 걸어놓은 주소는 있는거 같은데.. 쩝;
아시는분은 댓글로 남겨주시면 제작자 블로그 링크를 추가 하도록 하겠습니다.

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

Comment +2

  • 트랙백 감사합니다. ExternalInterface를 이용해 JS의 API를 사용하는 것은 좋은 방법이긴 하지만 약점도 있습니다. 바로 웹브라우져에서 동작하지 않고 독립 Flash Player에서 동작하거나 AIR에서 동작하는 경우입니다. 이때는 ExternalInterface를 이용하는 것자체가 무의미합니다. 물론 반드시 웹브라우져에서 동작한다는 것을 가정한다면 상관없습니다. ^^

    • 브라우저에서만 가능 하다는 내용은 기본기쪽을 쓰면서 언급 하기는 했습니다.

      아무래도 작업 하는쪽이 웹쪽만 작업 하다보니 별 생각없이 사용하게 되는거 같어요 ㅎㅎ

ExternalInterface가 뭐하는건지 잘 모르는 분은
2009/12/30 - [Flex/ExternalInterface] - ExternalInterface 기본기  이 글을 보고 오면 됩니다.

ExternalInterface 가 자바스크립트의 함수를 실행시키기만 하는것이 아니고.

이전 글에 있듯이.

Externalinterface.call("window.alert('아싸~')");
처럼 자바스크립트 내장객체를 호출하거나 메서드를 실행하는것도 가능합니다.
이걸 이용해서 예전에 유행했던 윈도우 창 흔들기를 간단하게 구현해보도록 하겠습니다.



버튼을 클릭해보세요. 파일을 업로드 했습니다 ㅎㅎ

역시 아쉽게도 글쓰는 곳이 파일 업로드가 안되서 이 블로그를 흔드는것은 시범을 보여드릴수가 없고 어플리케이션 하나 만들어서 아래소스를 붙여넣기 하고 실행해보시면 됩니다.

import mx.controls.Alert;

private var timer:Timer;
private var dir:Boolean = false;
private var offsetX:int;
private var offsetY:int;

private function shakeWindow():void
{
	offsetX = int(Math.random() * 20);
	offsetY = int(Math.random() * 20);
	
	timer = new Timer(30, 10);
	timer.addEventListener(TimerEvent.TIMER, shakeIt);
	timer.addEventListener(TimerEvent.TIMER_COMPLETE, stopShake);
	timer.start();
}

private function shakeIt(e:TimerEvent):void
{
	if(dir)
	{
		offsetX = -offsetX;
		offsetY = -offsetY;
	}
	dir = !dir;
	ExternalInterface.call("window.moveBy("+offsetX+","+offsetY+")");
}

private function stopShake(e:TimerEvent):void
{
	timer.removeEventListener(TimerEvent.TIMER, shakeIt);
	timer.removeEventListener(TimerEvent.TIMER_COMPLETE, stopShake);
}

스크립트 블록은 이렇게 되고 버튼 하나 만들어서 shakeWindow()를 호출해주시면 됩니다.

자바스크립트의 window.moveBy(x,y) 함수는 현재의 윈도우 위치에서 x, y 만큼 윈도우를 이동시켜주는 함수 입니다.

간단하게 offset 값을 정해주고 해당 타이머가 돌아가는동안 윈도우를 움직인다.
이걸로 끝입니다.

간단한 것이 가능하다면 복잡한것도 가능합니다.

브라우저 쿠키를 AS 3.0 만으로 만들고 삭제하고 읽어들이고 할수도 있습니다.
AS3 에는 같은 기능을 하는 SharedObject가 있지만 프로젝트에 따라서 쿠키를 사용해야 하는 경우가 있는데 이런경우 html에 추가 해야 하는 불편 없이 추가 할수 있습니다.

이부분은 이미 잘 만들어진 것이 있기 때문에 따로 구현하지 않고 링크로만 대신합니다.


저 링크에서 보듯이 Externalinterface를 사용해서 뭘 할것이냐는 전적으로 아이디어에 달려있는것 같습니다.
그냥 간단하게 정보 확인용으로만 사용할수도 있고. javascript에서만 가능한것들과 연계해서 뭔가 훨씬 재밌는것들을 찾아낼수도 있지 않을까 싶습니다.


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

Comment 0


ExternalInterface는 swf 파일을 싸고 있는 컨테이너 html 의 자바스크립트와 통신하기 위한 인터페이스 입니다.

ExternalInterface.call을 사용해서 html에 있는 자바스크립트 함수를 실행 시킬수 있고
ExternalInterface.callback을 사용해서 자바스크립트에서 SWF의 함수를 실행시킬수 있습니다.

이전 AS 2.0 에서는 fscommand()가 자바스크립트와의 통신에 사용되었는데 Player 9 버전 이후에는 Externalinterface를 사용하도록 권장하고 있습니다.

html과 함께 사용되는 것이기 때문에 swf만 실행시키거나 stand alone player에서 실행시키면 디버그 창을 만날수있습니다.

ExternalInterface가 중요한 이유가 C# 등 윈도우프로그램을 사용할경우 C#이 플레이어 api를 가지고 ExternalInterface를 구현해 놓으면 SWF와 윈도우 프로그램간의 통신 프로토콜로도 사용할수 있습니다.

addCallback(functionName:String, closure:Function):void
[정적] 컨테이너로부터 호출 가능하도록 ActionScript 메서드를 등록합니다.

functionName은 자바스크립트에서 플레시로 호출할때의 이름입니다. closure는 해당 이름을 플레시플레이어가 받았을때 호출할 함수 입니다.

addCallback 에서 String으로 키워드를 받고 그것에 closure에서 정의된 함수를 호출하는 구조이기 때문에 callback 에서 자바스크립트가 부르는 함수는 실제로 swf의 함수 이름이 아닐수도 있습니다.

콜백을 등록하기 전에 ExternalInterface.available을 사용해서 swf를 싸고 있는 컨테이너가 ExternalInterface를 지원 하는지 확인 후에 등록하는 것이 좋습니다.

call(functionName:String, ... arguments):*
[정적] 0개 이상의 인수를 전달하는 Flash Player 컨테이너에 의해 노출된 함수를 호출합니다.

call 함수는 자바스크립트의 함수 이름을 직접 지정하게 됩니다.
call 에서 부르는 함수는 실제 자바스크립트 함수의 이름이어야 합니다.

ExternalInterface.call 함수가 유용한 이유는 자바스크립트 함수를 단지 호출하는게 문제가 아니라 함수 실행 위치가 컨테이너로 옮겨간다는데 있습니다.

무슨소리냐 하면. functionName이 꼭 이름만 호출해야 할 필요가 없다는거죠. 저기에 html상에서 실행될수 있는 자바스크립트 함수가 직접 들어갈수 있습니다.

ExternalInterface.call("window.alert('아싸~')");
이렇게 하게 되면 Flex의 Alert 함수가 아닌 윈도우얼럿이 뜨게 됩니다.

Flex에서 IFrame 컴포넌트를 사용한다거나 하게 되면. 레이어가 플렉스보다 위쪽에 생기게 되어서 Flex Alert을 사용하게 되면 알람메시지가 IFrame컴포넌트보다 하위에 나와서 보이지 않게 되는데 이럴때 유용하게 사용할 수 있습니다.

자바스크립트 정리된 사이트가 갑자기 필요하게 되는 순간입니다.. -ㅅ-;;;

기본적인 내용은 여기서 마치고 다음 글에서 실제 이걸로 활용할수 있는 것들에 대해서 써보겠습니다.
ExternalInterface의 예제 들은 검색에 쳐보면 많이 나오기 때문에 패스 하도록 하겠습니다.

더큰 이유는 제가 이글을 쓰고있는곳이 파일첨부가 안됩니다..
보안프로그램과 방화벽때문에.. ㅠㅠ
부득이하게 이번 글은 예제 파일은 없이 갑니다 -ㅅ-~

Tip.

ExternalInterface는 동기로 동작하게 됩니다. 일반적인 플렉스 처럼 비동기가 아니고 콜백을 받거나 call을 하게 되면 플렉스함수가 실행되지 않고 멈추게 됩니다.
위에서 나온 alert을 띄우는것을 가지고 테스트 해보시면 됩니다.
Alert이 뜨는 순간 그 다음에 있는 플렉스 함수들은 실행되지 않습니다.

call을 호출하고 그 결과를 받을때에도 이벤트방식으로 동작하지 않기 때문에 결과가 올때까지 swf는 멈춰있게 됩니다. 테스트 해보시고 적용하실때 고민해서 적용하시는것이 자잘한 버그를 생산하지 않는 원동력이 됩니다..

다음 포스트에서는 응용편을 써볼까 합니다. 그럼..~
저작자 표시 비영리 동일 조건 변경 허락
신고

Comment 0

by 퍼플린 | 2009/07/30 13:53

selectedIndex를 변경시켜주는 시점을 결정하기 위해서 list가 업데이트가 끝났다는 이벤트를 받아서 완료되면 인덱스를 수정해주는 것이다. 이처럼 Flex나 Flash는 몇몇 method를 제외하고는 비동기로 동작하기 때문에 데이터의 수정이나 삭제 입력시에 ...

 

위글에서 이어지는 포스트이니 이전글을 보고 오는것도 좋다.
------------------------------------------------------

 

이전 글에서 리스트 내부의 아이템을 위로 옮기는 걸 했는데.

보면 알겠지만 뭔가 부족하다..

 

리스트가 길어질경우 현제 옮기고 있는 아이템이 리스트에서 보이지 않고 스크롤을 해야 보이기 때문에 위치이동이 많을 경우 스크롤을 내려서 확인 하고 하는등 애로사항이 꽃피게된다.

 

그래서 이번에는 하는김에 상하로 움직이는 것과 움직임에 맞춰서 스크롤 포지션을 옮겨주는 것을 구현해서 마무리 짓도록 하자.

 

Flex에는 horizontalScrollPosition, verticalScrollPosition, maxhor..등등..

스크롤 위치에 관한 속성이 미리 정의 되어있기 때문에 저걸 살짝만 건드려주면 원하는 결과를 얻을수 있다.

 

여기서는 List 컴포넌트를 사용하고 있기 때문에 가로 스크롤은 제외하고 세로스크롤만을 가지고 만든다.

 

스크롤포지션 값은 리스트에서 보여지는 갯수(rowCount)와 전체 개수를 기준으로 결정된다.

이것을 maxVerticalScrollPosition 값으로 알수 있다.

 

만약 데이터가 4개 이고 rowCount가 4라면.. maxVerticalScrollPosition값은 0이다.

데이터가 4개이고 rowCount가 3이라면 maxVerticalScrollPosition 값은 1이다.

한화면에 보여줄수 있는 것이 세개인데 데이터는 네개 이므로 아래로 한번더 스크롤 할수 있다.- 이런뜻이다.

 

이것만 알고 있으면 쉽게 구현이 가능하다.

 

소스코드를 보자.

 

 

import mx.events.FlexEvent;

private var arrData:Array;
private var idx:int;
private var posFlag:Boolean;

private function init():void
{
	arrData = [];
	arrData.push({label:"데이터1", data:"data1"});
	arrData.push({label:"데이터2", data:"data2"});
	arrData.push({label:"데이터3", data:"data3"});
	arrData.push({label:"데이터4", data:"data1"});
	arrData.push({label:"데이터5", data:"data2"});
	arrData.push({label:"데이터6", data:"data3"});
	arrData.push({label:"데이터7", data:"data1"});
	arrData.push({label:"데이터8", data:"data2"});
	arrData.push({label:"데이터9", data:"data3"});
	list.dataProvider = arrData;
}

private function posUp():void
{
	if(list.selectedItem && list.selectedIndex != 0)
	{
		idx= list.selectedIndex;
		posFlag = true;
		
		var tmpArr:Array = arrData.splice(idx,1);
		arrData.splice(idx -1,0,tmpArr[0]);
		list.dataProvider = arrData;
		list.addEventListener(FlexEvent.UPDATE_COMPLETE, setListIndex);
	}
}

private function posDown():void
{
	if(list.selectedItem && list.selectedIndex != arrData.length-1)
	{
		idx = list.selectedIndex;
		posFlag = false;
		
		var tmpArr:Array = arrData.splice(idx,1);
		arrData.splice(idx+1,0,tmpArr[0]);
		list.dataProvider = arrData;
		list.addEventListener(FlexEvent.UPDATE_COMPLETE, setListIndex);
	}
}

private function setListIndex(e:FlexEvent):void
{
	list.removeEventListener(FlexEvent.UPDATE_COMPLETE, setListIndex);
	list.selectedIndex = idx-1;
	
	if(posFlag)
	{
		list.selectedIndex = idx-1;
	}
	else
	{
		list.selectedIndex = idx+1;
	}
	var pos:int = list.selectedIndex-list.rowCount+1;
	list.verticalScrollPosition = (pos > 0)? pos:0;
}

 

추가된것은 별게 없다

 

posUp 함수는 선택된놈을 위로 올리는것 posDown은 내리는것 posFlag는 위로 올리는지 아래로 내리는지에 대한 Flag이다.

list는 List 컴포넌트고 해당 리스트의 rowCount는 4로 세팅되어있다.

 

마지막 setListIndex 함수를 보면

var pos:int = list.selectedIndex-list.rowCount+1;
list.verticalScrollPosition = (pos > 0)? pos:0;

 

이부분이 스크롤을 세팅해주는 부분이다.

 

selectedIndex를 기준으로 스크롤포지션을 세팅해준다.

조금만 보면 쉽게 이해할 수 있을꺼라 생각된다.

 

 

posUp 함수와 posDown 함수는 인덱스를 +1 해주느냐 -1 해주느냐의 차이밖에 없기 때문에 합쳐서 만들수도 있을꺼고. posFlag같은 경우도 이벤트를 받아서 처리한다던지 하게 되면 소스코드는 훨씬 간단하게 수정될 것이다.

 

선택된 놈이 가운데에 오게 한다거나 하는 것도 숫자놀이를 조금 하면 쉽게 세팅할 수 있을꺼라 생각한다.

 

이부분은.. 혹시나 이글을 읽는분께 -ㅅ-;;

신고

Comment 0

리스트의 아이템을 선택해서 위치를 위로 올려주는 프로그램이다.

 

private var arrData:Array;
private var idx:int;
			
private function init():void
{
	arrData = [];
	arrData.push({label:"데이터1", data:"data1"});
	arrData.push({label:"데이터2", data:"data2"});
	arrData.push({label:"데이터3", data:"data3"});
	list.dataProvider = arrData;
}
			
private function posUp():void
{
	if(list.selectedItem && list.selectedIndex != 0)
	{
		idx= list.selectedIndex;
		var tmpArr:Array = arrData.splice(idx,1);
		arrData.splice(idx -1,0,tmpArr[0]);
		list.dataProvider = arrData;
		list.selectedIndex = idx-1;
		trace(list.selectedIndex);
	}
}

 

List 컴포넌트의 아이디는 list 이고 버튼을 클릭하면 posUp()함수가 호출된다.

 

dataProvider 로 쓰이는 arrData 배열을 편집해서 선택한것을 한단계 위로 올리고 그것을 다시 List의 dataProvider로 세팅해준다.

 

이후에 list의 selectedIndex를 idx-1 값으로 즉 이전 선택 인덱스-1의 값으로 만들어준다. 위 코드로 만들게 되면 한번은 실행이 정상적으로 되는 것처럼 보인다.

세번째 데이터를 선택하고 버튼을 눌러서 posUp()을 호출하면 화면상에서 보이는 위치와 선택된 상태가 바뀐다. 즉, selectedIndex가 2 에서 1로 바뀌는 것처럼 보인다.

하지만 아랫줄의 trace 값에서 list.selectedIndex를 찍어보면 1이 아니고 2로 나온다..

 

이전의 selectedIndex가 그대로 호출되는 것이다.

 

 한번더 버튼을 누르면 2번이 1번으로 올라가지 않고 2,3 번이 위치를 바꾸는 기현상이 일어난다.

 

 

이 코드를 정상 작동하게 만들기 위해서는 아래처럼 수정한다.

import mx.events.FlexEvent;
			
private var arrData:Array;
private var idx:int;
			
private function init():void
{
	arrData = [];
	arrData.push({label:"데이터1", data:"data1"});
	arrData.push({label:"데이터2", data:"data2"});
	arrData.push({label:"데이터3", data:"data3"});
	list.dataProvider = arrData;
}
			
private function posUp():void
{
	if(list.selectedItem && list.selectedIndex != 0)
	{
		idx= list.selectedIndex;
		var tmpArr:Array = arrData.splice(idx,1);
		arrData.splice(idx -1,0,tmpArr[0]);
		list.dataProvider = arrData;
		list.addEventListener(FlexEvent.UPDATE_COMPLETE, handler);
	}
}
			
private function handler(e:FlexEvent):void
{
	list.selectedIndex = idx-1;
}

 

selectedIndex를 변경시켜주는 시점을 결정하기 위해서 list가 업데이트가 끝났다는 이벤트를 받아서 완료되면 인덱스를 수정해주는 것이다.

 

 

이처럼 Flex나 Flash는 몇몇 method를 제외하고는 비동기로 동작하기 때문에 데이터의 수정이나 삭제 입력시에 해당 행위가 끝났는지를 파악해서 이후의 작업을 하는 것이 중요한 경우가 있다.

list.dataProvider = arrData;
list.selectedIndex = 1;

이렇게 실행시켰을때 코드 한줄 차이로 바로 아래에 있지만 list.selectedIndex=1이 호출되는 시점에서 list.dataProvider가 null 일수도 있다.

이런 문제는 발생하는 경우도 있고 어떤때는 정상적으로 실행되는것처럼 보이기도 하기 때문에 인지하지 못하고 있으면 어디에서 문제가 생겼는지 찾지 못해서 머리를 쥐어뜯게 되는 경우가 생길수도 있다.

 

이벤트를 이해하고 정상적으로 처리될 수 밖에 없도록 프로그래밍을 하는것이 최고의 방법일 것이다.

 

 

List 상하로 변경하기 2 - 스크롤포지션 세팅

신고

Comment +3

  • 이 포스트 덕에 많은 도움이 되었습니다.
    감사합니다.

  • selectedIndex를 변경하는건 바로 변경이 되지를 않네요. selectedItem을 이용하여 array에서 지정된 index에 해당하는 아이템을 찾은후에

    list.selectedItem = arrData[idx-1];

    위와 같이 하면 이벤트 처리 없이 선택할 수도 있겠네요.

    • 도움이 됐다니 다행입니다.

      item이나 index나 사실은 똑같은 놈을 가리키고 있으니 어느쪽을 사용해도 별 문제는 없다고 생각합니다.

      다만 이벤트를 사용하는 것이 아무래도 런타임에 발생하는 오류의 빈도를 줄일 수 있다라는 것이 포스트의 요지입니다.

      관리만 잘된다면 이벤트가 편하긴 하죠. 직관적으로 코드 읽기는 역시 직접 세팅이 좋지만요 ㅎㅎ

package {
	import flash.display.Bitmap;
	import flash.display.Loader;
	import flash.display.Sprite;
	import flash.events.Event;
	import flash.events.MouseEvent;
	import flash.net.URLRequest;
	
	[SWF(width="1000", height="1000", frameRate="24", backgroundColor="#FFFFFF")]
	
	public class bitmapDataHandle extends Sprite
	{
		private var container:Sprite;
		private var bitmap:Bitmap;
		private var loader:Loader;
		public function bitmapDataHandle()
		{
			loader = new Loader();
			loader.load(new URLRequest("cat.jpg"));
			loader.contentLoaderInfo.addEventListener(Event.COMPLETE, loadHandler);
		}
		
		private function loadHandler(e:Event):void
		{
			loader.contentLoaderInfo.removeEventListener(Event.COMPLETE, loadHandler);
			bitmap = loader.content as Bitmap;
			loader.unload();
			
			container = new Sprite();
			container.addChild(bitmap);
			container.addEventListener(MouseEvent.CLICK, clickHandler);
			addChild(container);
			
		}
		
		private function clickHandler(e:MouseEvent):void
		{
			var pixelValue:uint = bitmap.bitmapData.getPixel(mouseX,mouseY);
			trace("x : "+mouseX+" y: "+mouseY);
			trace("선택한 곳의 색상값 : "+pixelValue.toString(16));
		}
	}
}

까페에 질문글이 올라와서 급조한 테스트..

bitmapData에는 pixel을 이용해서 여러가지 작업을 할수 있다. 그중에 getPixel은 선택한 곳의 해당 픽셀값을 가져온다.

 

getPixels () 메서드  

public function getPixels(rect:Rectangle):ByteArray
언어 버전: ActionScript 3.0
런타임 버전: AIR 1.0, Flash Player 9

픽셀 데이터의 사각형 영역에서 바이트 배열을 생성합니다. 픽셀별로 부호 없는 정수(곱하지 않은 32비트 픽셀 값)를 바이트 배열에 씁니다.

매개 변수

rect:Rectangle — 현재 BitmapData 객체의 사각형 영역입니다.

반환값
ByteArray — 해당 Rectangle의 픽셀을 나타내는 ByteArray입니다.

오류
TypeError — rect가 null입니다.
getPixels () 메서드  
public function getPixels(rect:Rectangle):ByteArray
언어 버전: ActionScript 3.0
런타임 버전: AIR 1.0, Flash Player 9

픽셀 데이터의 사각형 영역에서 바이트 배열을 생성합니다. 픽셀별로 부호 없는 정수(곱하지 않은 32비트 픽셀 값)를 바이트 배열에 씁니다.

매개 변수

rect:Rectangle — 현재 BitmapData 객체의 사각형 영역입니다.

반환값
ByteArray — 해당 Rectangle의 픽셀을 나타내는 ByteArray입니다.

오류
TypeError — rect가 null입니다.

 

테스트 함수 만들고 포토샵에서 나오는 컬러 코드와 비교 해봤을때.

 

x : 530 y: 376
선택한 곳의 색상값 : 584b42

 

이렇게 출력된 좌표의 색상값을 포토샵에서 찍어보면 594b42 로 나온다.

 

몇군데 좌표를 테스트 해봤지만 포토샵과 똑같이 나오지는 않는것 같다.

하지만 사람눈으로 구별할수 있을만큼의 차이는 나지 않는다.

신고

Comment +2

웹이나 디비에서 데이터를 가져온 텍스트의 경우 저장하는 방법에 따라서 개행이 /r/n 으로 넘어오는 경우가 있다.

 

이 경우 플레시는 한줄 개행이 아니고 두줄로 처리 해서 텍스트 필드의 라인간격이 두배로 나오는 경우가 생기게 되는데 이때는 넘어오는 텍스트에서 /r을 검색해서 삭제 해버리면 된다.

 

이럴때 사용하는 함수..

private function removeCarriageReturn(str:String):String
{
	var myPattern:RegExp = /r/g;
	str = str.replace(myPattern,"");
	return str;
}

함수이름이 내용보다 긴것 같지만 무슨 상관이랴~

 

/r 은 캐리지리턴이라부르고 입력커서를 라인의 맨 앞으로 보낸다

/n은 라인피드라고 해서 커서를 다음줄로 내리게 된다.

 

 예전에는 두개가 모여야 한줄의 개행이었던걸로 알고 있는데 요즘에는 걍 n만 쓰면 대부분이 정상적으로 라인이 바뀐다.

신고

Comment 0

  private function mcToBitmap(mc:MovieClip):Array
  {
   var bitmapData:BitmapData = new BitmapData(mc.width, mc.height,true);
   var bitmapArray:Array = [];
   var totalFrame:int = mc.scenes[0].numFrames;
   trace(totalFrame);
   
   for(var i:int=0; i<totalFrame; i++)
   {
    mc.gotoAndStop(i+1);
    bitmapData.draw(mc);
    bitmapArray.push(bitmapData.clone());
    bitmapData.dispose();
   }
   
   return bitmapArray;
  }

 

무비클립을 인수로 받고 각 프레임의 bitmapData를 추출해서 Array로 넘겨준다.

반사이미지 만들어주는 오픈소스를 가져다 썼는데 이놈이 인수를 무비클립만 받는다.. 거기서 반사 이미지까지 만들어진 무비클립을 내쪽에서 처리하려면 다시 비트맵 데이터로 넘겨 받아야 하는경우가 있어서 만들게 됐다.

 

무비클립은 Scene이 한개인놈만 되고.. 안에서 scenes.length로 for문을 한번더 돌리면 모든 씬의 모든 프레임에 있는 데이터를 bitmapData로 가져올수도 있다.

 

뭐 필요한 경우가 있겠지 -ㅅ-;;;;

신고

Comment +2

  • kisspa 2009.08.10 14:05 신고

    totalFrame이 1보다 크면 위의 소스는 에러가 납니다.

    bitmapData.dispose();
    는 객체를 반환하는것으므로...for문 밖에 넣어주던가 해야 겠네요

    • 감사합니다~

      이게 쓸려고 했다가 상황이 바껴서 다르게 처리하게 되서 안쓰게 되버려서 여러가지 경우에 대한 테스트를 하지 못했군요.

      시간이 되면 테스트 해보고 수정하도록 하겠습니다~

AS3 에서 폰트를 동적으로 embed 하기 위해서 폰트 SWF파일을 만들게 되는데 용량을 줄이기 위해서 필요한 설정이 unicodeRange이다.

 

숫자나 영문 한글 라틴 지원한다면 중국어 일어등 필요한 범위만을 embed하게 되므로 용량이 줄어들지만 범위 이외의 문자는 표시 되지 않기 때문에 그 범위를 벗어나는 텍스트는 절대 나오지 않는다 라는 확신이 있을때 지정해서 사용하는편이 좋다.

 

 괜히 개발 해놨다가 글이 안올라가는거 같아요 이런 소리 듣고 나면 버그가 있는줄 알고 검내 찾다가.. 범위 이외라서 올라간 글이 단지 안보이는 것뿐이라는 것을 알고나면 허탈해질수도 있다.

package
{
	import flash.display.Sprite;

	public class Default extends Sprite
	{
		[Embed(systemFont='나눔고딕 Bold', fontName='나눔고딕 Bold', mimeType = "application/x-font"
		 , unicodeRange = "U+0020-U+007E,U+1100-U+11F9,U+3000-U+303F,U+3131-U+318E,U+327F-U+327F,U+AC00-U+D7A3,U+FF01-U+FF60,U+0020-U+0020,U+0041-U+005A,U+0061-U+007A,U+0030-U+0039,U+002E-U+002E,U+0020-U+002F,U+0030-U+0039,U+003A-U+0040,U+0041-U+005A,U+005B-U+0060,U+0061-U+007A,U+007B-U+007E")]
		public static var embedFont:Class;
	}
}

 

나눔고딕을 포함하는 SWF를 만드는 코드이다.

 

Embed 할때 source로 ttf 폰트파일 주소를 쓰지 않고 systemFont를 써서 만들었는데 ttf를 직접 주소를 주고 만들었을때 cs4 문제인지 fp10의 문제인지 폰트가 보이지 않는 문제가 있다.

 

- 이부분은 정확한 문제를 찾아서 포스팅을 작성해야할것 같다.

외국 포럼에서도 CS4 사용해서 embed Font를 만들때 Bold, Italic 이 보이지 않는다는 말들이 있는데 해결방법을 아직 못찾았다.

 

 ****** Unicode Range **********

영문 : U+0020-U+0020,U+0041-U+005A,U+0061-U+007A

숫자 : U+0030-U+0039,U+002E-U+002E

한글 : U+0020-U+007E,U+1100-U+11F9,U+3000-U+303F,U+3131-U+318E,U+327F-U+327F,U+AC00-U+D7A3,U+FF01-U+FF60

라틴 : U+0020-U+002F,U+0030-U+0039,U+003A-U+0040,U+0041-U+005A,U+005B-U+0060,U+0061-U+007A,U+007B-U+007E

 

이외에 일어나 중국어 등은.. -ㅅ-;; 너무 길어서 패스..

신고

Comment 0

폰트 동적 추가하기 fontKR.swf 에 font Class가 들어있고 linkage 네임은 font_kr로 되어있다.
package {
import flash.display.Loader;
import flash.display.Sprite;
import flash.events.Event;
import flash.net.URLRequest;
import flash.text.AntiAliasType;
import flash.text.Font;
import flash.text.TextField;
import flash.text.TextFieldAutoSize;
import flash.text.TextFormat;

public class FontLoadTest extends Sprite
{
private var fontLibrary:Class;
private var font:Font;

public function FontLoadTest()
{
init();
}

private function init():void
{
loadFont("fontKR.swf");
}

private function loadFont(url:String):void
{
var loader:Loader = new Loader();
loader.contentLoaderInfo.addEventListener(Event.COMPLETE, fontLoaded);
loader.load(new URLRequest(url));
}

private function fontLoaded(e:Event):void
{
fontLibrary = e.target.applicationDomain.getDefinition("font_kr") as Class;
Font.registerFont(fontLibrary);
var fontArray:Array = Font.enumerateFonts(false);
font = fontArray[0];
trace(font.fontName);

buildText();
}

private function buildText():void
{
var tf:TextField = new TextField();
tf.defaultTextFormat = new TextFormat(font.fontName,40,0);
tf.embedFonts = true;
tf.antiAliasType = AntiAliasType.ADVANCED;
tf.autoSize = TextFieldAutoSize.LEFT;
tf.text = "나눔고딕 Light ℃";

addChild(tf);
}
}
}
fontLibrary = e.target.applicationDomain.getDefinition("font_kr") as Class; 로딩된 폰트가 들어있는 SWF를 linkage이름으로 클래스화 시키고 Font.registerFont(fontLibrary); 폰트에 등록하면 사용할수 있다.
신고

Comment 0