오랜만에 Adobe Labs를 가봤는데 마침 Flash Builder 4, ColdFusion Builder, Flex 4 SDK가 베타 딱지를 떼고 정식으로 배포되기 시작했네요.

  • Adobe ColdFusion Builder
  • Adobe Flash Builder™ 4
  • Adobe Flex® 4 SDK

    위 링크를 통해 자세한 정보를 볼 수 있습니다.
  • 추가사항 1
    정영훈님께서 유용한 댓글을 달아주셨습니다. 이클립스에 Flash Builder를 Plug-in 형태로 설치할 때 멈추는 현상이 있는데 아래 내용으로 문제를 해결할 수 있습니다.

    설치할때 우선 압축을 풀게 되는데 그 경로를 지정할 수가 있습니다.
    그 압축을 푸는 경로에 하글이 섞여 있으면 설치가 안됩니다.

    xp에서는 바탕화면 에 압축을 풀게 딜포트 설정으로 나옵니다
    그 경로를 한글이 없는 경로로 수정하면 설치가 가능합니다.


     

    글쓴이 : 지돌스타(http://blog.jidolstar.com/667)

    Frame 메타데이터 태그는 Flex 기반에서 만든 Flash Application에 Preloader를 구현하기 위해서 가장 많이 사용합니다. 실제로 Flex를 뜯어보면 [Frame(factoryClass="mx.managers.SystemManager")] 부분이 존재합니다. 이때 이 SystemManager가 바로 Preloader 역할을 합니다. 재미있게도 이렇게 유용한 Frame 메타데이터 태그에 대한 어떠한 공식문서를 저는 본적이 없다는 겁니다. Flex기반이지만 ActionScript 3.0으로만 개발할때 꼭 필요한 요소인데도 말이죠.

    각설하고,
    지금 말하고자 하는 것은 Flash Builder 4, Flex 4 기반에서 ActionScript 3.0 기반 프로젝트를 만들어 Frame 메타데이터 태그를 사용할때 다음과 같은 에러메시지가 난다는 겁니다.(에러 안날 수 있습니다. 하지만 제 프로젝트에서는 나버리네요.)

    1172: mx.skins.spark:BorderSkin 정의를 찾을 수 없습니다.
    1202: 정의되지 않은 속성 BorderSkin(mx.skins.spark 패키지)에 액세스했습니다.


    이쯤되면 엄청 짜증나죠. 

    ActionScript 3.0기반에서는 chart.swc, spark.swc 같은 것은 사용하지 않죠? 여기서 해결 아이디어가 나옵니다. Flash Builder나 Flex Builder에서 프로젝트를 생성하면 .actionScriptProperties 파일이 생성됩니다. 이 안에는 Flex 컴파일러인 mxmlc를 통해 컴파일할때 필요한 설정이 들어가 있지요. 내부를 살펴보면 <excludedEntries>부분이 보입니다. 이 부분은 기본 Flex SDK에 있는 SWC나 SWZ중에 ActionScript 3.0 기반에서 제작시에 필요없는 SWC, SWZ를 포함해서 컴파일하지 않겠다는 것을 명시하는 겁니다. 그런데 Frame 메타데이터 태그를 사용하면 mxmlc컴파일러가 mx.skins.spark:BorderSkin가 들어가는 코드를 만들어주는데 그게 없어서 에러를 던지는 겁니다. 이 클래스는 sparkskin.swc에 포함되어 있는데 이게 <excludeEntries>에 포함되어 있습니다. 그러므로 <excludedEntries>부분을 주석처리를 하거나 삭제하면 sparkskin.swc를 컴파일시 참조할 수 있습니다.  결국 이렇게 하면 깔끔하게 에러가 없어집니다. 명확히 그 이유를 확인하고 싶으시다면 -keep-generated-actionscript 컴파일 옵션을 사용해보세요.

    참고로 Flash Builder에서 .actionScriptProperties 파일은 숨겨져 있습니다. 숨김설정을 푸는 방법을 간단히 소개하자면...

    1. Package Explorer에서 Filters를 선택합니다.


    2. Flex Package Explorer Filters 창이 뜨면 체크되어 있는 모든 항목 풉니다. .actionScriptProperties만 풀어도 됩니다.


    3. 이제 자신의 프로젝트에 .actionScriptProperties 파일을 볼 수 있습니다.


    추가사항 1
    댓글로 hika님께서 Embed 태그문제를 거론해주셨습니다. mxmlc 컴파일러는 Embed 메타데이터 태그를
    사용하면 중간에 mx.core.BitmapAsset, mx.core.MovieClipAsset, mx.core.FontAsset과 같은 클래스를 확장한 클래스를 만들어줍니다. 그렇기 때문에 이들 클래스가 있는 swc를 프로젝트 내에 포함해야겠지요. 정확히 알고 싶다면-keep-generated-actionscript 컴파일 옵션을 이용해 bin-debug에 들어간 generated 폴더를 확인해 보시면 됩니다.

    Flash 애플리케이션을 만드는데 사용되는 SWC는 playerglobal.swc 뿐입니다. 하지만 mxmlc를 사용해 Embed, Frame 메타데이터와 같은 편리한 기능을 사용하려면 그외에 swc를 포함해줘야합니다. Flash Builder에서 ActionScript 3.0 프로젝트를 만들면 playerglobal.swc, textLayout.swc, flex.swc, utilities.swc를 포함되는 것은 다 이런 이유 때문입니다. 나머지 swc는 모두 <excludedEntries>로 제외해버리죠. 하지만 Frame을 썼을때 위에서 언급한 에러가 발목을 잡으므로 <excludedEntries>를 제거함으로써 문제를 해결할 수 있었다는 것을 언급한겁니다. 그때그때 상황에 맞게 .actionScriptProperties를 가지고 조절하시면 된다는 말씀~ ^^; (간단한건데.. 왜이렇게 설명을 장황하게 했는지 저도 모르겠습니다. ㅎㅎ)

    추가사항 2
    CSS파일이 없습니다라는 에러도 <excludedEntries>를 제거하면 없어집니다. -defaults-css-url 컴파일 옵션이나 .actionScriptProperties내에 <!-- --> 빈 주석을 달아 없애는 것 외에 또 하나의 팁!

    참고글
    Flex에서 순수 AS3로 Preloader 구현하기
    AS3로 SystemManager 의 Preloading을 흉내내기
    ActionScript 3.0에서 Preloader 구현 및 Default css file not found 경고 메시지 없애기
    Preloader의 기본 css 파일이 없습니다(Default css file not found) 경고 문제
    프로젝트 템플릿 ant
    Preloaders in AS3

    글쓴이 : 지돌스타(http://blog.jidolstar.com/665)
     


    2009년 한국천문연구원 아마추어 천문학 발전을 위한 위탁과제로 만든 대한민국 인터넷 천문대(http://realsky.org) 입니다.

    이 사업은 언제 어디서나 누구나 인터넷을 통해 쉽게 밤하늘을 관측하고 정보를 얻을 수 있는 것을 목표로 최초 제작되었습니다. 2009년 6월 부터 12월까지 프로젝트가 진행되었으며 이를 위해 미국 우주망원경연구소의 디지털하늘탐사(Digitized Sky Survey, DSS) 데이터를 이용해 전천의 밤하늘 이미지를 가공했고 이것을 보여주기 위해 Adobe Flash 기술을 이용했습니다.

    이미지의 편집 및 이용권한은 천문노트(http://astronote.org) 에게만 있습니다.

    클라이언트 엔진은 ActionScript 3.0이며 일부 Flex 4를 이용해 개발했습니다. 서버측 통신은 ZendAMF를 이용했습니다. 최초 개발한 만큼 아직 부족하나 앞으로 유용하고 다양한 형태로 개발할 계획입니다.

    http://realsky.org

    글쓴이 : 지돌스타(http://blog.jidolstar.com)


    ACC의 오창훈님과 이문국님이 직접 제작한 Adobe Flash Platform 탐구생활 동영상입니다.

    동영상의 좌측 상단을 누르시면 Flash Builder와 Flash Catalyst의 베타버전을 무료로 다운로드 받을 수 있습니다. ^^

    Flash Builder 100배 즐기기



    Flash Catalyst 100배 즐기기


    출처 : http://www.adoberia.co.kr/trial_down/main.html

    최근에 Flex 4.0 기반으로 Adobe AIR 애플리케이션을 개발하는 프로젝트를 맡았다. 현시점(2010.2.1) Flex 4.0 SDK에 함께 있는 AIR SDK 버전은 한단계 구버전인 1.5.2이기 때문에 가장 최신 버전인 1.5.3을 기존 Flex 4.0 SDK에 덮어씌운 상태에서 개발을 진행했었다. 문제없이 개발할 수 있다.

    그런데 애플리케이션 배포시 문제가 발생했다. AIR 애플리케이션을 설치하면 설치 디렉토리 밑에 META-INF/AIR/publisherid 파일이 존재해야 한다. 그런데 이 파일이 없는 것이다. publisherid는 인증서의 고유 ID를 담고 있다. 이 파일이 없는 상태에서 배포, 업데이트 모두 잘되지만 badge에서 이미 설치된 AIR 애플리케이션이 있는지 여부를 판단할 수 없었다. 사실 배포,업데이트가 잘 되기 때문에 이 상태로 배포해도 문제는 없지만 너무 꺼름칙했다. (AIR의 장점중 하나가 쉽게 설치된 AIR 애플리케이션과 Flash의 연동이 큰 장점 아니던가....)

    이 문제의 원인을 찾고자 별짓을 다하다가 SDK 문제라는 것에 귀결을 내리게 되었다. 문제는 Flex 4.0 SDK에 AIR 1.5.3 SDK를 덮은것에서 비롯되었다. 기존 Flex 4.0 SDK에서 AIR 1.5.2 기반으로 만들어 배포하면 Publisher ID 파일이 있지만 AIR 1.5.3으로 덮어씌우고 배포하면 이 파일이 없었던 것이다. 개발, 컴파일까지 모두 잘되는데 말이다. ㅎㅎ 

    결국 나는 Flex 4.0 기반으로 애플리케이션을 거의 완성한 상태라서 AIR 1.5.2 기반으로 배포할 수 밖에 없게 되었다. 편리하고 빠른 개발을 위해 Flex 4.0를 선택했지만 AIR 버전이 구버전으로 인한 문제점은 정말 예상치 못한 일이었다.

    개발하다 보면 별에 별일이 많은데... 정말 이렇게 크리티컬한 상황까지 보게 되니 정말 웃기기 까지 한다.

    질문?
    Flex 4.0에 AIR 1.5.3을 덮어씌워도 publisher id 파일이 누락되지 않도록 하는 방법은 뭘까요? 혹시 알게되면 댓글이나 트랙백 부탁합니다.

    추가 1
    Flex 3의 최신버전인 Flex 3.5의 경우에는 AIR 1.5.3 SDK가 함께 있는데... Flex 4는 왜???? ㅡㅡ;;

    추가 2
    Flex 4.0 SDK의 beta 2 버전은 4.0.0.10485 이다. 이 버전은 AIR 1.5.2 SDK이다. 하지만 최신 Nightly Builder 버전으로 다운로드 받으면 AIR 1.5.3이 기본이다. 그냥 이걸로 개발해야겠다.

    추가 3
    Adobe AIR 1.5.3 부터는 PublisherID가 기본적으로 제거됩니다. http://blog.jidolstar.com/655

    글쓴이 : 지돌스타(http://blog.jidolstar.com/652)


    지난 2009년 12월 18일에 프로젝트 코드명 Squiggly(스퀴글리)가 Prerelease판으로 업데이트 되었다.
    http://labs.adobe.com/technologies/squiggly/

    Squiggly는 Flash Player 10, AIR 1.5이상에서 동작한다. 이 엔진은 ActionScript 3.0 기반이며 영어로 문장에서 사용된 단어를 영어사전의 그것과 비교하여 단어의 철자를 체크한다. 이번 업데이트는 TLF(Adobe Text Layout Framework)를 기반으로 하는 Flex 4 Spark 컴포넌트를 지원하도록 했다. Squiggly는 Flex Builder, Flash Builder, Flash CS4등 다양한 툴에서 사용할 수 있다. 단지 아쉬운 것은 영어단어만 체크한다는 점이다.


    다음 페이지는 Squiggly 데모이다.
    http://labs.adobe.com/technologies/squiggly/demo/

    개발자는 언제든지 예제소스와 ASDoc을 아래 링크에서 받아볼 수 있다.


    이것을 쓸지 모르겠지만 아무튼 알아두고 있어 나쁠 것도 없으니....

    참고
    http://adnaru.com/242

    글쓴이 : 지돌스타(http://blog.jidolstar.com/640)

     

    지난 2009년 11월 어도비(Adobe)에서는 AIR 2.0 Beta를 새롭게 발표하면서 퍼포먼스 향상과 더 많은 OS의 자원을 사용할 수 있는 API 기능을 추가해서 발표했다. Flash Player 10.1 Prerelease와 함께 발표된 Adobe AIR 2.0의 새로운 기능 및 추가/개선 사항에 대해서 정리해보았다.

    참고로 이 글은 Elad Elrom의 AIR 2 Enhancements Complete Overview  와 AIR 2 Rerelease Note를 참고해서 정리한 것이다.




    1. 새로운 기능 (이번 페이지에서 다룸)
      - File Promises
      - Screen reader
      - Native Processes
      - New networking support
      - Global Error Handling
      - Packaging an AIR application in a native installer
    2. 기존 API에 대한 추가된 기능 (다음 편에 다룸)
      - Max Size of NativeWindow
      - Idle time-out
      - Mac Vector Printing
      - Database Transaction Savepoints
      - Microphone Access API
      - Open file with Default Application
    3. 플랫폼 인식 관련 API (다음 편에 다룸)
      - Multi-touch functionality
      - Mass Storage Device Detection
    4. 퍼포먼스 향상 (다음 편에 다룸)


    출처 : http://tinyurl.com/ydxevnu




    Flash Builder 4 beta 2에서 Adobe AIR 2.0로 개발 환경 만들기 

    여기에서 보여지는 예제는 모두 Flash Builder 4 beta 2에 기본으로 설치된 Flex 4 SDK를 이용한다. 하지만 AIR 2.0로 테스트해보기 위해서는 AIR 2.0 SDK를 따로 받아 다음과 같은 과정이 필요하다.

    Windows 환경이라면 다음과 같이 하면 되겠다.

  • 만약 Flash Builder를 설치 안했다면 다음 링크를 통해 받는다.
    1. AIR 2.0 SDK와 Runtime을 다운로드 받는다.
    2. 다운받은 Runtime을 실행해 설치한다.
    3. Flash builder가 설치된 sdks/4.0.0과 sdks/3.4.1 폴더를 복사해서 sdks 폴더안에 붙인뒤 4.0.0_AIR2.0, 3.4.1_AIR2.0을 만든다. 이렇게 되면 sdks 폴더안에는 3.4.1, 4.0.0, 3.4.1_AIR2.0, 4.0.0_AIR2.0 이름을 가진 4개의 SDK가 만들어진다.
      본인의 경우는 C:/Program Files/Adobe/Adobe Flash Builder Plug-in Beta 2/sdks 내에 있다. 
    4. AIR 2.0 SDK는 압축을 풀고 그안에 있는 내용을 방금 만든 4.0.0_AIR2.0, 3.4.1_AIR2.0 폴더에 복사한다.
    5. Flash Builder를 실행한다.
    6. 메뉴에서 Window > Preference 로 들어간다.
    7. 창이 뜨면 왼쪽 메뉴에서 Flash builder > Installed Flex SDKs를 선택한다.
    8. 우측에 Add버튼을 눌러 위에서 새로 만든 AIR 2.0을 위한 SDKs들을 선택한다.  
      Flex SDK Name은 각각 Flex 3.4 AIR 2.0, Flex 4.0 AIR 2.0 등으로 이름을 바꿔도 된다.
      본인의 경우 아래 경로가 되겠다.
      C:/Program Files/Adobe/Adobe Flash Builder Plug-in Beta 2/sdks/3.4.1_AIR2.0/
      C:/Program Files/Adobe/Adobe Flash Builder Plug-in Beta 2/sdks/4.0.0_AIR2.0/
      아래처럼 Flex 4.0으로 체크되어 있다면 AIR 2.0기반의 Flex 4.0 SDK를 디폴트로 바꾸자.
    9. 이제 모든 개발환경이 완료되었다. 자신의 프로젝트 생성시에 원하는 SDK를 선택하면 되겠다.  물론 아래에서 소개하는 예시를 따르려면 Flex 3든 Flex 4든 AIR 2.0 SDK를 선택해야겠다.


    Mac 환경이라면 다음과 같이 하면 되겠다.

    Mac에서 AIR 2.0 Beta 개발환경 만들기 - Flash Builder 4 Beta 2 기반

    참고로 Flash CS4나 CS3, 드림위버나 기타 다른 IDE에서 개발하시는 분들에게는 본인의 경험이 없어 정보를 줄 수 없다.

  •  
    1. Adobe AIR 2.0 새로운 기능

    1.1 IPv6 지원
    AIR 2.0은 모든 Network API에 대해서 IPv6를 지원하게 되었다. 현재 인터넷에서 주로 사용하고 있는 IP 주소의 버전은 IPv4로서 무려 20년이 넘도록 사용하고 있다. IPv4는 32비트 주소체계를 가지며 4,294,967,296개의 제한된 갯수의 주소를 가진다. IPv6는 이 부족함을 해소하기 위해 만들어진 IP 주소 체계이다. IPv6는 128비트 주소체계를 가진다. IPv6가 일반적으로 사용되고 있는 것은 아니지만  가까운 미래에 IPv6가 범용적으로 도입되어 네트워크 IP 주소체계가 더욱 확장될 수 있으므로 미리 지원될 수 있도록 한 것으로 여겨진다. IPv6에 대한 자세한 내용은 아래 링크를 참고한다.




    1.2  UDP(User Datagram Protocol) 소켓, Server 소켓, TLS/SSL 소켓 지원
    AIR 2.0에서는 더욱 다양한 소켓 통신 관련 클래스들이 추가되었다. 이전버전까지 반쪽짜리 였다면 이제 어느정도 구색을 갖춘듯 하다. 

    AIR 2.0에서 지원하는 네트워크에 관련된 내용은 다음 링크를 참고한다.


    서버 소켓 : ServerSocket 클래스 
    기존 AIR 버전에서는 반쪽 소켓통신만 지원되었다. 즉, 클라이언트는 만들 수 있었지만 서버측을 만들지 못했다. 이번 AIR 2.0에서 서버 소켓이 지원됨에 따라 이를 이용한 다양한 네트워크 애플리케이션을 만들 수 있을 것으로 보인다.

    서버 소켓은 TCP(Transmissin Control Protocal)을 기반으로 한다. TCP는 연결지향 통신 규약으로 인터넷 상의 컴퓨터들 사이에 데이터를 통신하기 위해 IP와 함께 사용된다. TCP는 데이터의 통신 중간에 데이터가 유실되더라도 신뢰도를  체크해서 완벽한 데이터를 통신할 수 있는 신뢰형 기반의 데이터 통신시 사용된다. 일반적인 웹브라우져를 이용한 통신데이터는 모두 TCP를 근간으로 한다.



    AIR 2.0에서 지원하는 서버 Socket는 ServerSocket 클래스에서 관장한다.



    UDP 소켓 : DatagramSocket 클래스
    UDP는 User Datagram Protocal의 약어로 IP를 사용해 네트워크내에서 컴퓨터들간에 데이타 통신하기 위해 제한된 서비스만 제공하는 통신 규칙이다. TCP의 대안이기도 하며 IP와 함께 사용하면 UDP/IP라고 표현하기도 한다. UDP는 정확한 데이터를 전송할 의무가 주어지지 않은 비연결지향성을 가져 데이터의 흐름에 신뢰를 할 수 없는 것이 특징이다. TCP처럼 통신을 하기전 상대방을 확인하는 절차가 없기 때문에 데이터 정확도는 떨어질 수 있지만 빠른 데이타 전송에 유리하다. 그래서 음악 및 비디오 실시간 스트리밍 서비스에 잘 사용하는 프로토콜이다.



    AIR 2.0에서는 UDP 통신을 DatagramSocket 클래스와 DatagramSocketDataEvent 클래스를 지원하고 있다. 더욱 자세한 내용은 아래 링크를 참고한다.



    Secure 클라이언트 소켓 : SecureSocket 클래스 
    소켓서버에 접속하기 위해 SSLv4(Secure Socket Layer version 4) 또는 TLSv1(Transport Layer Security version 1) 기반으로 하는 프로토콜을 이용하여 SecureSocket 클래스로 접속할 수 있다. Secure 소켓을 이용하면 암호화된 데이터 전송으로 서버인증(server authentication), 데이터 무결성(data integrity), 메시지 기밀성(message confidentiality)등의 장점을 가진다. 




    1.3 네트워크 정보 
    AIR 2.0의 API로 네트워크 정보를 얻어낼 수 있는 클래스인 NetworkInfo가 추가되었다. 이 클래스를 이용하면 사용자의 기기의 Mac Address, IP정보 등의 네트워크 정보를 얻어올 수 있다.


    Adobe 문서에서 보여지는 예제에서는 var networkInfo:NetworkInfo = new NetworkInfo() 처럼 생성해서 사용하도록 되어 있는데 잘못된 것이다. var networkInfo:NetworkInfo = NetworkInfo.networkInfo; 처럼 static 속성으로 접근해야한다.
    그리고 NetworkInterface에 networkInterfaceDefault 속성 또한 존재하지 않는다.  
    package { 
    	import flash.display.Sprite; 
    	import flash.net.InterfaceAddress; 
    	import flash.net.NetworkInfo; 
    	import flash.net.NetworkInterface; 
    	
    	public class NetworkInformationExample extends Sprite 
    	{ 
    		public function NetworkInformationExample() 
    		{ 
    			var networkInfo:NetworkInfo.<NetworkInterface> = NetworkInfo.networkInfo;
    			var interfaces:Vector.<NetworkInterface> = networkInfo.findInterfaces(); 
    			
    			if( interfaces != null ) 
    			{ 
    				trace( "Interface count: " + interfaces.length ); 
    				for each ( var interfaceObj:NetworkInterface in interfaces ) 
    				{ 
    					trace( "\nname: "             + interfaceObj.name ); 
    					trace( "display name: "     + interfaceObj.displayName ); 
    					trace( "mtu: "                 + interfaceObj.mtu ); 
    					trace( "active?: "             + interfaceObj.active ); 
    					//trace( "default?: "         + interfaceObj.networkInterfaceDefault ); 
    					trace( "parent interface: " + interfaceObj.parent ); 
    					trace( "hardware address: " + interfaceObj.hardwareAddress ); 
    					if( interfaceObj.subInterfaces != null ) 
    					{ 
    						trace( "# subinterfaces: " + interfaceObj.subInterfaces.length ); 
    					} 
    					trace("# addresses: "     + interfaceObj.addresses.length ); 
    					for each ( var address:InterfaceAddress in interfaceObj.addresses ) 
    					{ 
    						trace( "  type: "           + address.ipVersion ); 
    						trace( "  address: "         + address.address ); 
    						trace( "  broadcast: "         + address.broadcast ); 
    						trace( "  prefix length: "     + address.prefixLength ); 
    					} 
    				}             
    			} 
    		}     
    	} 
    }
    

     
    위 프로그램은 NetworkInfo를 사용하는 아주 간단한 예시이다. Flash Builder 4 에서 이 예시를 테스트해볼때는 AIR 프로젝트 생성시 마지막에 mxml대신 as로 확장자를 두고 생성해서 테스트 하면 되겠다.


    1.4 DNS lookup
    AIR 2.0에서부터 DNSResolver 클래스를 제공하여 DNS(Domain Name System) 정보를 찾을 수 있게 되었다. 정보를 찾게되면 DNSResolverEvent가 발생한다. 

    DNS는 각 도메인 이름(가령 examples.com)을 IP주소로 가리킨다. 일반적으로 사람은 111.123.431.11와 같은 IP주소보다 examples.com처럼 도메인 이름과 같은 단어조합을 더욱 인지하기 쉽다. 이러한 이유로 실제로는 도메인 이름을 사용하면 DNS를 걸쳐 이에 해당하는 IP주소로 연결시켜 관련 서버로 접속하게 한다. AIR 2.0에서 제공하는 DNSResolver 클래스를 이용하면 이러한 관계를 조사해볼 수 있게 된다. 

    IP주소는 IPv4(32비트) 또는 IPv6(128비트)를 사용할 수 있다. 

    DNSResolver 클래스의 lookup() 함수를 통해 host이름과 레코드 형태(record type)을 넘겨주면 DNSResolverEvent.LOOK_UP 이벤트를 발생하여 관련 레코드 타입에 대한 정보를 반환해준다. 
     



     다음은 예제이다.
    package
    {
    	import flash.desktop.NativeApplication;
    	import flash.display.Sprite;
    	import flash.events.DNSResolverEvent;
    	import flash.events.ErrorEvent;
    	import flash.net.dns.AAAARecord;
    	import flash.net.dns.ARecord;
    	import flash.net.dns.DNSResolver;
    	import flash.net.dns.MXRecord;
    	import flash.net.dns.PTRRecord;
    	import flash.net.dns.SRVRecord;
    	import flash.utils.getQualifiedClassName;
    	
    	public class DNSResolverExample extends Sprite
    	{
    		private var lookupCount:int = 0;
    		public function DNSResolverExample()
    		{
    			//Create the resolver object
    			var resolver:DNSResolver = new DNSResolver();
    			resolver.addEventListener( DNSResolverEvent.LOOKUP, lookupComplete );
    			resolver.addEventListener( ErrorEvent.ERROR, lookupError );
    			
    			//Look up records
    			resolver.lookup( "yahoo.com", ARecord );
    			resolver.lookup( "yahoo.com", AAAARecord );
    			resolver.lookup( "yahoo.com", MXRecord );
    			resolver.lookup( "209.191.93.53", PTRRecord );
    			resolver.lookup( "_sip._tcp.yahoo.com.", SRVRecord );
    		}
    		
    		private function lookupComplete( event:DNSResolverEvent ):void
    		{
    			trace("-------------------------------------");
    			trace( "Query string: " + event.host );
    			trace( "Record type: " +  flash.utils.getQualifiedClassName( event.resourceRecords[0] ) + 
    				", count: " + event.resourceRecords.length );
    			for each( var record:* in event.resourceRecords )
    			{
    				if( record is ARecord ) trace( record.name + " : " + record.address );
    				if( record is AAAARecord ) trace( record.name + " : " + record.address );
    				if( record is MXRecord ) trace( record.name + " : " + record.exchange + ", " + record.preference );
    				if( record is PTRRecord ) trace( record.name + " : " + record.ptrdName );
    				if( record is SRVRecord ) trace( record.name + " : " + record.target + ", " + record.port +
    					", " + record.priority + ", " + record.weight );
    			}    
    			if( ++lookupCount == 5 )
    			{
    				NativeApplication.nativeApplication.exit();
    			}
    		}
    		
    		private function lookupError( error:ErrorEvent ):void
    		{
    			trace("-------------------------------------");
    			trace("Error: " + error.text );
    			if( ++lookupCount == 5 )
    			{
    				NativeApplication.nativeApplication.exit();
    			}
    		}
    	}
    }
    



    1.5 스크린 리더 지원(Screen Reader Support)

    AIR 2.0은 스크린 리더를 지원하기 위한 Accessibility 클래스가 추가되었다. 스크린 리더는 시각 장애 사용자들을 위한 보조 기술로서 화면 내용을 오디오 버전으로 제공하는 것이다. 이 클래스는 현재 Windows 환경에서만 제공된다. 이것이 지원되는지 확인하려면 Capabilities.hasAccessibility 정적속성이 true이어야 한다. 버튼, 무비클립, 텍스트필드와 같은 객체에 액세스 가능한 속성을 얻고 설정하기 위해 DisplayObject.accessibilityProperties 속성을 사용하며 이 값이 바뀌었다는 것을 알려주기 위해 Accessibility.updateProperties() 정적메소드를 이용한다.

    이 기능을 사용하기 위해 프로젝트의 컴파일 옵션을 추가해야한다. Flash builder 4에서 해당 프로젝트를 선택후 메뉴의 Project > Properties를 선택한다. 새창이 나오면 Flex Compiler를 선택후 컴파일 옵션에 Generate accessible SWF file을 선택후 OK 버튼을 누른다.


    package {
    	import flash.accessibility.Accessibility;
    	import flash.desktop.NativeApplication;
    	import flash.display.Sprite;
    	import flash.display.StageAlign;
    	import flash.display.StageScaleMode;
    	import flash.utils.setTimeout;
    	
    	public class AccessibilityExample extends Sprite {
    		public static const BUTTON_WIDTH:uint = 90;
    		public static const BUTTON_HEIGHT:uint = 20;
    		
    		private var gutter:uint = 5;
    		private var menuLabels:Array = new Array("PROJECTS", "PORTFOLIO", "CONTACT");
    		private var menuDescriptions:Array = new Array("Learn more about our projects"
    			, "See our portfolio"
    			, "Get in touch with our team");
    		
    		public function AccessibilityExample() {
    			configureAssets();
    			NativeApplication.nativeApplication.autoExit = true;
    			stage.align = StageAlign.TOP_LEFT;
    			stage.scaleMode = StageScaleMode.NO_SCALE;
    			stage.nativeWindow.activate();
    			setTimeout(updateAccessibility, 2000); 
    		}
    		
    		private function updateAccessibility():void {
    			trace("Accessibility.active: " + Accessibility.active);
    			if(Accessibility.active) {
    				Accessibility.updateProperties();
    			}
    		}
    		
    		private function configureAssets():void {
    			var child:CustomAccessibleButton;
    			for(var i:uint; i < menuLabels.length; i++) {
    				child = new CustomAccessibleButton();
    				child.y = (numChildren * (BUTTON_HEIGHT + gutter));
    				child.setLabel(menuLabels[i]);
    				child.setDescription(menuDescriptions[i]);
    				addChild(child);
    			}
    		}
    	}
    }
    
    import flash.accessibility.AccessibilityProperties;
    import flash.display.Shape;
    import flash.display.SimpleButton;
    import flash.display.Sprite;
    import flash.events.Event;
    import flash.text.TextFormat;
    import flash.text.TextFormatAlign;
    import flash.text.TextField;
    
    class CustomAccessibleButton extends Sprite {
    	private var button:SimpleButton;
    	private var label:TextField;
    	private var description:String;
    	private var _name:String;
    	
    	public function CustomAccessibleButton(_width:uint = 0, _height:uint = 0) {
    		_width = (_width == 0) ? AccessibilityExample.BUTTON_WIDTH : _width;
    		_height = (_height == 0) ? AccessibilityExample.BUTTON_HEIGHT : _height;
    		
    		button = buildButton(_width, _height);
    		label = buildLabel(_width, _height);
    		
    		addEventListener(Event.ADDED, addedHandler);
    	}
    	
    	private function addedHandler(event:Event):void {
    		trace("addedHandler: " + this._name);
    		var accessProps:AccessibilityProperties = new AccessibilityProperties();
    		accessProps.name = this._name;
    		accessProps.description = description;
    		accessibilityProperties = accessProps;
    		removeEventListener(Event.ADDED, addedHandler);
    	}
    	
    	private function buildButton(_width:uint, _height:uint):SimpleButton {
    		var child:SimpleButton = new CustomSimpleButton(_width, _height);
    		addChild(child);
    		return child;
    	}
    	
    	private function buildLabel(_width:uint, _height:uint):TextField {
    		var format:TextFormat = new TextFormat();
    		format.font = "Verdana";
    		format.size = 11;
    		format.color = 0xFFFFFF;
    		format.align = TextFormatAlign.CENTER;
    		format.bold = true;
    		
    		var child:TextField = new TextField();
    		child.y = 1;
    		child.width = _width;
    		child.height = _height;
    		child.selectable = false;
    		child.defaultTextFormat = format;
    		child.mouseEnabled = false;
    		
    		addChild(child);
    		return child;
    	}
    	
    	public function setLabel(text:String):void {
    		label.text = text;
    		this._name = text;
    	}
    	
    	public function setDescription(text:String):void {
    		description = text;
    	}
    }
    
    class CustomSimpleButton extends SimpleButton {
    	private var upColor:uint = 0xFFCC00;
    	private var overColor:uint = 0xCCFF00;
    	private var downColor:uint = 0x00CCFF;
    	
    	public function CustomSimpleButton(_width:uint, _height:uint) {
    		downState = new ButtonDisplayState(downColor, _width, _height);
    		overState = new ButtonDisplayState(overColor, _width, _height);
    		upState = new ButtonDisplayState(upColor, _width, _height);
    		hitTestState = new ButtonDisplayState(upColor, _width, _height);
    		useHandCursor = true;
    	}        
    }
    
    class ButtonDisplayState extends Shape {
    	private var bgColor:uint;
    	private var _width:uint;
    	private var _height:uint;
    	
    	public function ButtonDisplayState(bgColor:uint, _width:uint, _height:uint) {
    		this.bgColor = bgColor;
    		this._width = _width;
    		this._height = _height;
    		draw();
    	}
    	
    	private function draw():void {
    		graphics.beginFill(bgColor);
    		graphics.drawRect(0, 0, _width, _height);
    		graphics.endFill();
    	}
    }
    


    1.6 네이티브 프로세스의 직접 실행과 상호 작용( Launching and Interacting with Native Processes )
    네이티브 프로세스에 대해서 본인도 처음 접하는 용어라 해석하기 어려웠는데, 이해한바 각 운영체제에 실행될 수 있는 코드라고 생각하면 될 것 같다. 가령, 윈도우에서는 exe, dll, ocx등이겠고 맥이라면 dmg, app 등이 아닐까 생각한다.

    AIR 2.0부터 기존의 애플리케이션을 실행해 AIR 애플리케이션과 표준입출력을 통한 상호작용할 수 있는 기능이 추가되었다. 가령, C언어로 만든 프로그램을 자신의 AIR 애플리케이션과 함께 배포했다고 하자. AIR 애플리케이션이 이 C언어로 만든 프로그램을 실행하고 표준입출력을 이용해 통신이 가능해진다. 또는 이미 설치된 Editplus에 File을 넘겨서 실행하고 종료시점도 알 수 있다.

    자세한 내용은 다음을 참고한다.

    이 기능을 수행하려면 반드시 AIR 디스크립터 파일에 다음 코드를 꼭 넣어야 한다.


    <supportedProfiles>extendedDesktop</supportedProfiles>
    


    아래는 Editplus를 이용해 Text파일을 열고 Editplus가 닫힐 때 감지하는 간단한 Flex 4를 이용한 AIR 2.0 애플리케이션 코드이다. NativeProcess와 NativeProcessStartupInfo 클래스의 용도를 잘 살펴보자.
    <?xml version="1.0" encoding="utf-8"?>
    <s:WindowedApplication xmlns:fx="http://ns.adobe.com/mxml/2009" 
    					   xmlns:s="library://ns.adobe.com/flex/spark" 
    					   xmlns:mx="library://ns.adobe.com/flex/halo">
    	<fx:Script>
    		<![CDATA[
    			import flash.events.NativeProcessExitEvent;
    			
    			public function executeNativeProcess():void 
    			{
    				var executable:File = new File("C:/Program Files/EditPlus 3/editplus.exe"); //c:/Windows/System32/notepad.exe
    				var workingDirectory:File = new File("c:/");
    				
    				var nativeProcess:NativeProcess = new NativeProcess();
    				
    				if (NativeProcess.isSupported) 
    				{
    					trace("Native Process Supported");
    				}
    				
    				var nativeProcessStartupInfo:NativeProcessStartupInfo = new NativeProcessStartupInfo();
    				nativeProcessStartupInfo.executable = executable;
    				nativeProcessStartupInfo.workingDirectory = workingDirectory;
    				
    				var args:Vector.<String> = new Vector.<String>();   
    				args.push("c:/LAN.log"); // open file that was given with the executable application 
    				nativeProcessStartupInfo.arguments = args;
    				
    				nativeProcess.addEventListener( NativeProcessExitEvent.EXIT, onExitError );
    				
    				try {
    					nativeProcess.start(nativeProcessStartupInfo);
    				} catch (error:IllegalOperationError) {
    					trace("Illegal Operation: "+error.toString());
    				} catch (error:ArgumentError) {
    					trace("Argument Error: "+error.toString());
    				} catch (error:Error) {
    					trace ("Error: "+error.toString());
    				}
    				
    				if (nativeProcess.running) 
    				{
    					trace ("Native Process Support");
    				}       
    			}
    			
    			public function onExitError(event:NativeProcessExitEvent):void 
    			{
    				trace( "Native Process Exit code: "+event.exitCode );
    			}            
    			
    		]]>
    	</fx:Script>
    	
    	<s:Button id="button"
    			  label="Open File foobar.txt with text editor"
    			  click="executeNativeProcess();"
    			  width="250" />
    	
    </s:WindowedApplication>
    

    Mac인 경우 Mac에 설치되어 기본으로 설치되어 있는 텍스트 에디터를 실행할 수도 있다. 이것도 별다른 설정없이 같은 방법으로 실행할 수 있다. new File()에는 "/Applications/TextEdit/TextEdit.app/Contents/MacOS/TextEdit"와 "/" 로 대체하고 args.push()부분은 foobar.txt가 데스크톱에 있을때 "/User/사용자계정/Desktop/foobar.txt"로 대체해서 실행하면 된다. 

    NativeProcess 클래스는 단순히 실행과 종료만 담당하지 않는다. AIR 애플리케이션과 Native 실행 소스간에 통신도 가능하다. 다음 링크는 HTML/Javascript, Flex, Flash 개발자들을 위한 예제이다. AIR코드 뿐 아니라 C코드도 있으니 꼭 다운받아 실행해보길 바란다.

    다음과 같이 Flex 4 코드로 변경해봤다.


    <?xml version="1.0" encoding="utf-8"?>
    <s:WindowedApplication xmlns:fx="http://ns.adobe.com/mxml/2009" 
    					   xmlns:s="library://ns.adobe.com/flex/spark" 
    					   xmlns:mx="library://ns.adobe.com/flex/halo" 
    					   width="350" height="150"
    					   applicationComplete="windowedapplication1_applicationCompleteHandler(event)">
    	<fx:Script>
    		<![CDATA[
    			import flash.events.ProgressEvent;
    			import flash.system.Capabilities;
    			import flash.utils.IDataInput;
    			
    			import mx.controls.Alert;
    			import mx.events.FlexEvent;
    			
    			private var process:NativeProcess;
    			
    			private function windowedapplication1_applicationCompleteHandler(event:FlexEvent):void {
    				//Native Process가 지원되는지 확인. 
    				//지원되려면 반드시 디스크립터에  <supportedProfiles>extendedDesktop</supportedProfiles>을 추가해야한다.
    				if( NativeProcess.isSupported ) {
    					launchEchoTest();
    				} else {
    					Alert.show("NativeProcess not supported.","Error");
    				}
    			}			
    			private function launchEchoTest():void {
    				//실행할 Native 애플리케이션 선택
    				var file:File = File.applicationDirectory;
    				file = file.resolvePath("NativeApps");
    				if( Capabilities.os.toLocaleLowerCase().indexOf("win") > -1 ) {
    					file = file.resolvePath("Windows/bin/echoTestWin.exe");
    				} else {
    					file = file.resolvePath("Mac/bin/echoTestMac");
    				}
    				
    				//Native Process를 진행하기 위한 정보 설정 
    				var info:NativeProcessStartupInfo = new NativeProcessStartupInfo();
    				info.executable = file;
    				
    				//Native Process생성
    				process = new NativeProcess();
    				
    				//보내고 받을때 발생하는 이벤트에 대한 처리 
    				process.addEventListener(ProgressEvent.STANDARD_OUTPUT_DATA, function(event:ProgressEvent):void {
    					var date:Date = new Date();
    					var bytes:IDataInput = process.standardOutput;
    					textReceived.text = bytes.readUTFBytes( bytes.bytesAvailable );
    					launchEchoTest();
    					lbDateStamp.text = date.toString();
    				} );
    				process.addEventListener(ProgressEvent.STANDARD_INPUT_PROGRESS, function(event:ProgressEvent):void {
    					process.closeInput();
    				} );
    				//Native Process 시작 
    				process.start( info );
    			}
    			private function writeData():void {
    				process.standardInput.writeUTFBytes( textToSend.text + "\n" );
    			}
    		]]>
    	</fx:Script>
    	<fx:Declarations>
    		<!-- Place non-visual elements (e.g., services, value objects) here -->
    	</fx:Declarations>
    	<s:VGroup paddingLeft="10" paddingRight="10" paddingTop="10" paddingBottom="10">
    		<mx:Form>
    			<mx:FormItem label="Text to send" direction="horizontal">
    				<s:TextInput id="textToSend"/>	
    				<s:Button label="writeData()" click="writeData()"/>
    			</mx:FormItem>
    			<mx:FormItem label="Text received">
    				<s:TextInput id="textReceived" editable="false"/>
    			</mx:FormItem>
    		</mx:Form>
    		<s:Label id="lbDateStamp"/>
    	</s:VGroup>
    </s:WindowedApplication>
    
    


    아래 코드는 AIR 애플리케이션과 통신할 C코드(Mac용)이다.
    #include <stdio.h>
    #include <stdlib.h>
    #include <sys/time.h>
    #include <sys/types.h>
    #include <fcntl.h>
    #include <unistd.h>
    
    #define BUFFER_SIZE 8192
    
    int main(int argc, char** argv)
    {
    	char buf[BUFFER_SIZE];
    	int  cnt;
    	
    	while ( !feof(stdin) )
    	{
    		cnt = read(STDIN_FILENO, buf, sizeof buf); 
    		if (-1 == cnt) 
    		{
    			perror("read");
    			exit(1);
    		}
    		if (0 == cnt)
    		{
    			// eof reached...
    			exit(0);
    		}
    
    		write(STDOUT_FILENO, buf, cnt ); 
    	}
    	
    	return 0;
    }
    
    




    NativeProcess의 standardInput과 standardOutput 속성은 각각 IDataOutput, IDataInput을 입력과 출력값으로 받고 있다. 그러므로 이 속성을 이용해 표준입출력(standard input/output)을 이용해 서로 통신하게 된다.

    매우 다양한 형태로 응용이 가능할 것이라 판단한다.


    1.7 File Promise 지원

    AIR 2.0부터 FilePromise 클래스가 새로 추가되어 드래그&드롭 형태로 리모트 파일을 AIR 애플리케이션 외부로 다운로드 받을 수 있게 되었다. URLFilePromise 클래스를 이용해 이 작업을 할 수 있다.

    이 기능은 Windows와 Mac에서만 지원되며(Clipboard.supportsFilePromise로 확인) 반드시 AIR 애플리케이션 외부로 드롭하는 경우만 해당한다. 일반적인 클립보드 복사&붙이기 기능으로는 할 수 없다.

    다음 코드는 예시이다.

     

    <?xml version="1.0" encoding="utf-8"?>
    <s:WindowedApplication xmlns:fx="http://ns.adobe.com/mxml/2009" 
    					   xmlns:s="library://ns.adobe.com/flex/spark" 
    					   xmlns:mx="library://ns.adobe.com/flex/halo"
    					   applicationComplete="windowedapplication1_applicationCompleteHandler(event)">
    	<fx:Script>
    		<![CDATA[
    			import mx.controls.Alert;
    			import mx.events.DragEvent;
    			import mx.events.FlexEvent;
    			
    			private var clipboard:Clipboard;
    			private function windowedapplication1_applicationCompleteHandler(event:FlexEvent):void {
    				clipboard = new Clipboard();
    				if( !clipboard.supportsFilePromise ) {
    					Alert.show( "File Promise를 지원하지 않습니다." );
    				}
    			}
    			private function onDragOut(event:DragEvent):void {
    				trace( event.target, event.type );
    				var items:Array = fileData.selectedItems;
    				var promises:Array = new Array();
    				for each (var item:Object in items) {
    					var filePromise:URLFilePromise = new URLFilePromise();
    					var request:URLRequest = new URLRequest(item.url);
    					filePromise.request = request;
    					filePromise.relativePath = item.name;
    					promises.push(filePromise);
    				}
    				clipboard.setData(ClipboardFormats.FILE_PROMISE_LIST_FORMAT, promises);
    				NativeDragManager.doDrag(fileData, clipboard);     
    			}
    			private function onDragOutComplete(event:NativeDragEvent):void {
    				trace( "Drag out complete");
    				clipboard = new Clipboard();
    			}            
    		]]>
    	</fx:Script>
    	
    	<fx:Declarations>
    		<mx:ArrayCollection id="arrColl">
    			<mx:source>
    				<fx:Array>
    					<fx:Object name="rhall.jpg" url="http://a1.twimg.com/profile_images/57117466/robert_m_hall_bio_photo_big_normal.jpg" />
    					<fx:Object name="bobjim.jpg" url="http://a1.twimg.com/profile_images/51723308/ryancampbell3_normal.jpg"/>
    					<fx:Object name="jenschr.jpg" url="http://a1.twimg.com/profile_images/43222252/jenschr_mugshot3_normal.jpg"/>
    					<fx:Object name="adamflater.jpg" url="http://a1.twimg.com/profile_images/21503622/Photo_8_normal.jpg"/>
    					<fx:Object name="reboog711.jpg" url="http://a1.twimg.com/profile_images/16984682/DSCF0044_normal.jpg"/>
    				</fx:Array>
    			</mx:source>
    		</mx:ArrayCollection>
    	</fx:Declarations>
    	
    	<mx:DataGrid id="fileData" dragEnabled="true"
    				 dataProvider="{arrColl}" 
    				 dragStart="onDragOut(event)"
    				 nativeDragComplete="onDragOutComplete(event)" width="100%">
    		<mx:columns>
    			<mx:DataGridColumn dataField="name" width="100"/>
    			<mx:DataGridColumn dataField="url"/>
    		</mx:columns>
    	</mx:DataGrid>
    	
    </s:WindowedApplication>
    




    1.8 Global Error Handling

    AIR 애플리케이션이 런타임 도중에 에러가 발생할 때 국부적 Error 처리는 이미 가능했다. 하지만 개발자가 애플리케이션의 어느 부분에서 Error가 발생할지 또는 문제가 발생할지 찾기 힘든 것이 현실이다. 국부적으로 일어나는 에러를 처지하지 못하는 경우 애플리케이션이 동작중에 어떤 부분에서 에러가 발생하든지 대처할 수 있도록 Global Error를 처리할 수 있는 기능이 AIR 2.0과 Flash Player 10.1에 추가가 되었다. 다음 글을 참고하자.

    Global Error를 처리하는데 UncaughtErrorEvent 클래스를 이용한다. 이름에서도 볼 수 있듯이 기존의 try..catch문으로 처리하지 못한 Error가 발생할 때 발생함을 알 수 있다.

    발생하는 UncaughtErrorEvent를 처리하기 위해 LoaderInfo 객체의 uncaughtErrorEvents 속성에 이벤트 처리를 위한 함수를 등록하면 된다.

    loaderInfo.uncaughtErrorEvents.addEventListener( UncaughtErrorEvent.UNCAUGHT_ERROR, handler );

    단, 로드된 SWF 객체의 경우에는 Loader의 uncaughtErrorEvents 속성에 이벤트 처리 함수를 등록한다.

    loader.uncaughtErrorEvents.addEventListener( UncaughtErrorEvent.UNCAUGHT_ERROR, handler );


    하나의 SWF가 다른 SWF에 로드되는 경우 UncaughtErrorEvent 이벤트 전파에 대한 처리가 필요하다.  A.swf가 B.swf를 Loader를 이용해 로드했다면 다음과 같은 순서로 LoaderInfo와 Loader 객체에 UncaughtErrorEvent 가 전파된다.

    1. (Capture phase) A.swf의 LoaderInfo
    2. (Capture phase) A.swf내의 Loader 
    3. (Target phase) B.swf의 LoaderInfo
    4. (Bubble phase) A.swf의 Loader
    5. (Bubble phase) A.swf의 LoaderInfo

    Loader객체의 uncaughtErrorEvents 속성은 결코 이벤트 전파시 target phase가 발생하지 않음에 주목하자. 그리고 이렇게 이벤트가 전파되는데는 디스플레이 리스트에 등록되어 있어야만 한다. 즉 addChild()된 객체어야한 한다. 중간에 이벤트 전파를 중단하려면 stopPropagation()이나 stopImmediatePropagation() 메소드를 이용하면 되겠다. 또한 ADL 기반나 디버그용 Flash Player에서 구동 경우 에러표시 다이얼로그가 뜨는 것을 방지하기 위해 UncaughtErrorEvent 이벤트 발생시 preventDefault() 메소드를 사용해 안뜨도록 할 수 있다.

    아래는 AIR 2.0 예제이다.

    package {
    	import flash.desktop.*;
    	import flash.display.*;
    	import flash.events.*;
    	public class UncaughtErrorEventExample extends Sprite {
    		public function UncaughtErrorEventExample()
    		{
    			var options:NativeWindowInitOptions = new NativeWindowInitOptions();
    			options.type = NativeWindowType.UTILITY;
    			
    			var window:NativeWindow = new NativeWindow(options);
    			window.width = 200;
    			window.height = 200;
    			window.title = "UncaughtErrorEventExample";
    			
    			window.stage.scaleMode = StageScaleMode.NO_SCALE;
    			window.stage.align = StageAlign.TOP_LEFT;
    			
    			window.activate();
    			window.addEventListener(Event.CLOSING, function($event:Event):void {
    				NativeApplication.nativeApplication.exit();
    			});			
    
    			var btn:Sprite = new Sprite();
    			btn.useHandCursor = true;
    			btn.buttonMode = true;
    			btn.graphics.clear();
    			btn.graphics.beginFill(0xFFCC00);
    			btn.graphics.drawRect(0, 0, 100, 50);
    			btn.graphics.endFill();
    			btn.addEventListener(MouseEvent.CLICK, clickHandler);			
    			window.stage.addChild(btn);
    			
    			loaderInfo.uncaughtErrorEvents.addEventListener(UncaughtErrorEvent.UNCAUGHT_ERROR, uncaughtErrorHandler);
    		}
    		private function uncaughtErrorHandler(e:UncaughtErrorEvent):void {
    			if (e.error is Error) {
    				var error:Error = e.error as Error;
    				// do something with the error
    				trace(error.errorID, error.name, error.message);
    			} else if (e.error is ErrorEvent) {
    					var errorEvent:ErrorEvent = e.error as ErrorEvent;
    					// do something with the error
    					trace(errorEvent.errorID);
    			} else {
    				// a non-Error, non-ErrorEvent type was thrown and uncaught
    				trace( " a non-Error, non-ErrorEvent type was thrown and uncaught " );
    			}
    			e.preventDefault();
    		}
    		private function clickHandler(event:MouseEvent):void
    		{
    			throw new Error("Gak!");
    		}
    	}
    }
    


    위 예제에서 볼 수 있듯이 AIR 애플리케이션의 노란 부분을 클릭하면 강제로 에러를 발생시킨다. 이때 이 에러를 처리하기 위한 try...catch문이 없으므로 UncaughtErrorEvent가 발생하고 그것을 처리하고 있다. 마지막에 e.preventDefault()를 둬서 에러표시 다이얼로그가 뜨는 것을 방지한다.


    1.9 Packaging an AIR application in a native installer

    AIR 2.0가 생소하게 다가오는 이유중에 하나는 바로 AIR 2.0을 설치할 때 과정이다. 뱃지(Badge)기능을 이용해 웹페이지에서 AIR 런타임 및 해당 AIR 애플리케이션을 다운로드 받을 수 있지만 그 과정이 일반 사용자에게 생소하게 다가올 수 있다. AIR 2.0에서는 이러한 생소함을 덜어주기 위해 각 OS별로 설치패키지를 따로 구성할 수 있도록 지원하고 있다. 가령, Windows는 exe, Mac은 dmg으로 설치 패키지를 구성할 수 있다는 말이다.

    AIR 2.0의 ADT의 옵션설정으로 설치 패키지를 만들 수 있다. 다음 링크를 참고한다.


    연관글






    정리하며 

    이번 글은 Adobe AIR 2.0 Overview 첫번째 글로 AIR 2.0에 추가된 새로운 기능에 대해서만 언급했다.  "기존 API에서 추가된 기능", "플랫폼 인식 관련 API", "퍼포먼스 향상"에 관련된 내용도 조만간 언급하겠다. 이 글을 읽고 AIR 2.0 기반으로 하는 다양한 애플리케이션 예제들이 많이 나왔으면 하는 바램이다.

    글쓴이 : 지돌스타(http://blog.jidolstar.com/629)
    처음 Flash Builder 4 beta 2를 설치하면 설치된 디렉토리에 sdks 디렉토리가 있고 이 안에 3.4.1, 4.0.0 의 두개의 디렉토리가 있다. 이 디렉토리는 SDK 두개의 버전을 말한다. 이는 윈도우(MS Windows)던 맥(Mac)이든 동일하다. 윈도우는 C:/Program Files/Adobe/Flash Builder 4 beta 2/sdks가 되겠고 맥이라면 /Application/Adobe Flash Builder 4 beta 2/sdks가 될 것이다.

    하지만 AIR 1.5개발을 위한 sdk들이며 새로운 AIR 2.0 Beta를 테스트해보기 위해서는 sdks를 변경해야한다. 윈도우에서는 Adobe Labs에서 AIR 2.0 Beta SDK를 다운로드 받아 해당 sdks안에 3.4.1, 4.0.0 디렉토리에 각각 복사하고 AIR 2.0 Runtime을 설치하는 것만으로 개발환경이 구축된다. (이미 윈도우환경에서 AIR 2.0 Beta를 Flash Builder 4에 적용하는 방법을 소개했었다. : http://blog.jidolstar.com/619)

    하지만 맥에서의 복사는  윈도우 운영체제에서 복사와 다른 개념을 가진다. 윈도우에서는 같은 디렉토리 이름의 경우 이전의 내용과 덮어쓰려는 내용을 합쳐버린다. 하지만 맥은 같은 이름의 디렉토리는 제거하고 복사할 디렉토리로 바꾼다. 이를 대치한다고 한다. 그래서 맥 사용자는 AIR 2.0 개발환경을 구축하기 위해 조금 복잡한 과정을 거쳐야한다.

    약간 복잡하더라도 리눅스를 조금만 다뤄봤다면 쉽게 이 문제를 해결할 수 있다.

    • 파인더에서 Application(응용 프로그램) > 유틸리티 에서 터미널을 실행한다. 
    • 만약 이전에 root 계정을 만든 적이 있다면 바로 su를 입력해 root권한으로 접근한다.
      아니면 sudo passwd root 를 입력해서 현재 계정의 암호를 입력하고 root암호를 설정후 성공적으로 입력하면 su로 접속해 방금 입력한 root암호로 접속한다. 참고로 root로 접속하면 명령 프롬프트가 $에서 #로 바뀐다.
    • 다운로드 받은 AIR 2.0 SDKs를 기존 sdks에 복사하기 위해 cp -rf orig_dir/* dest_dir를 이용한다.
      만약 AIR 2.0 SDKs를 자신의 홈에 복사해 두었다면 다음과 같이 터미널 창에 입력하여 강제 복사한다.
      cp -rf /Users/jidolstar/AIR2.0SDK/* /Applications/Adobe\ Flash\ Builder\ Beta\ 2/sdks/3.4.1/
      cp -rf /Users/jidolstar/AIR2.0SDK/* /Applications/Adobe\ Flash\ Builder\ Beta\ 2/sdks/4.0.0/

    윈도우와 맥의 디렉토리 복사에 대한 개념이 달라 본인도 몇시간 고민한 결과 찾아낸 방법이다. 좋은 정보가 되었으면 한다.


    추가사항 
    기존에 AIR 1.5 기반의 SDKs를 그대로 두면서 AIR 2.0도 필요할 때 개발하고 싶은 사람도 있을 것이다. 
    그런 경우에는 위의 설명에서 약간만 다르게 하면 되겠다. 위에서 설명한 2번째 내용에서 root로 접속하는 것까지는 같다. 

    1. Flash builder 4의 sdks 디렉토리에 있는 3.4.1과 4.0.0의 복사본을 만든다. 
      mkdir 3.4.1_AIR2.0
      mkdir 4.0.0_AIR2.0
      cp -R 3.4.1/* 3.4.1_AIR2.0
      cp -R 4.0.0/* 4.0.0_AIR2.0
    2. AIR 2.0 SDKs를 3.4.1_AIR2.0과 4.0.0_AIR2.0 폴더에 복사한다.
      cp -rf /Users/jidolstar/AIR2.0SDK/* /Applications/Adobe\ Flash\ Builder\ Beta\ 2/sdks/3.4.1_AIR2.0/
      cp -rf /Users/jidolstar/AIR2.0SDK/* /Applications/Adobe\ Flash\ Builder\ Beta\ 2/sdks/4.0.0_AIR2.0/
    3. Flash Builder 를 실행한다.
    4. 메뉴에서 Eclipse > Preference에 들어간다. (참고로 Window였다면 Window > Preference이다.)
    5. 창이 뜨면 왼쪽 메뉴에서 Flash builder > Installed Flex SDKs를 선택한다.
    6. 우측에 Add버튼을 눌러 위에서 새로 만든 AIR 2.0을 위한 SDKs들을 선택한다.  
      Flex SDK Name은 각각 Flex 3.4 AIR 2.0, Flex 4.0 AIR 2.0 등으로 이름을 바꿔도 된다.
      본인의 경우 아래 경로가 되겠다.
      /Applications/Adobe\ Flash\ Builder\ Beta\ 2/sdks/3.4.1_AIR2.0/
      /Applications/Adobe\ Flash\ Builder\ Beta\ 2/sdks/4.0.0_AIR2.0/
      본인은 아래처럼 AIR 2.0기반의 Flex 4.0 SDK를 디폴트로 잡았다.

    7. 이제 모든 개발환경이 완료되었다. 자신의 프로젝트 생성시에 원하는 SDK를 선택하면 되겠다. 
    8. 만약 프로젝트 중간에 AIR 1.5기반으로 만들다가 AIR 2.0기반으로 바꾸려면 해당프로젝트를 선택후 마우스 우클릭으로 Properties를 선택한다. 창이 뜨면 좌측 메뉴에서 Flex Compiler를 선택후 Flex SDK Version을 해당 SDK로 바꾸면 되겠다.


    어제 Adobe AIR에서 CPU 사용을 줄이는 방법에 대해서 소개했다. 이 글에 Hika님과 찬익님이 그에 대해 댓글을 달아주시며 다른 정보를 공유해주셨다. 정말 감사드린다. 이런 것이 블로그의 매력일 것이다. 블로그를 내 완벽한 지식을 전달하기 위해 쓴다는 것은 다소 어폐가 있다. 완벽한 것은 없다. 블로그는 지식 소통의 도구로 잘 활용하면 좋은 정보 공유를 통한 더욱 완전해지는 지식습득이 가능하게 한다. 난 지난 몇년간 블로그를 하면서 정말 몸소 체험했다. 지금 이글을 보고 있는 분들도 꼭 블로그를 하길 권장한다.

    각설하고. 찬익님이 Flex 4 기반에서 AIR 애플리케이션을 만들때 FrameRate를 조절하는 방법으로 backgroundFrameRate를 소개해주었다. 이것은 Event.ACTIVATE와 Event.DEACTIVATE를 이용해서 이미 Flex 4의
    WindowedApplication 클래스에 적용되어 있다. 아래 코드는 WindowedApplication 클래스의 일부이다.

    /**
    *  Constructor.
    *  
    *  @langversion 3.0
    *  @playerversion AIR 1.5
    *  @productversion Flex 4
    */
    public function WindowedApplication()
    {
    	super();
    
    	addEventListener(MouseEvent.MOUSE_DOWN, mouseDownHandler);
    	addEventListener(FlexEvent.PREINITIALIZE, preinitializeHandler);
    	addEventListener(FlexEvent.UPDATE_COMPLETE, updateComplete_handler);
    	addEventListener(FlexEvent.CREATION_COMPLETE, creationCompleteHandler);
    
    	var nativeApplication:NativeApplication = NativeApplication.nativeApplication;
    	nativeApplication.addEventListener(Event.ACTIVATE, nativeApplication_activateHandler);
    	nativeApplication.addEventListener(Event.DEACTIVATE, nativeApplication_deactivateHandler);
    	nativeApplication.addEventListener(Event.NETWORK_CHANGE, dispatchEvent);
    
    	nativeApplication.addEventListener(InvokeEvent.INVOKE, nativeApplication_invokeHandler);
    	initialInvokes = new Array();
    
    	//Force DragManager to instantiate so that it can handle drags from
    	//outside the app.
    	DragManager.isDragging;
    }
    
    /**
    *  @private
    *  Storage for the backgroundFrameRate property.
    */
    private var _backgroundFrameRate:Number = 1;
    
    /**
    *  Specifies the frame rate to use when the application is inactive.
    *  When set to -1, no background frame rate throttling occurs.
    *
    *  @default 1
    *  
    *  @langversion 3.0
    *  @playerversion AIR 1.5
    *  @productversion Flex 4
    */
    public function get backgroundFrameRate():Number
    {
    	return _backgroundFrameRate;
    }
    
    /**
    *  @private
    */ 
    public function set backgroundFrameRate(frameRate:Number):void
    {
    	_backgroundFrameRate = frameRate;
    }
    
    /**
    *  @private
    */
    private function nativeApplication_activateHandler(event:Event):void
    {
    	dispatchEvent(new AIREvent(AIREvent.APPLICATION_ACTIVATE));
    
    	// Restore throttled framerate if appropriate when application is activated.
    	if (prevActiveFrameRate >= 0 && stage)
    	{
    	    stage.frameRate = prevActiveFrameRate;  
    	    prevActiveFrameRate = -1;
    	}
    }
    
    /**
    *  @private
    */
    private function nativeApplication_deactivateHandler(event:Event):void
    {
    	dispatchEvent(new AIREvent(AIREvent.APPLICATION_DEACTIVATE));
    
    	// Throttle framerate if appropriate when application is deactivated.
    	// Ensure we've received an updateComplete on the chance our layout
    	// manager is using phased instantiation (we don't wish to store a
    	// maxed out (1000fps) framerate).
    	if ((_backgroundFrameRate >= 0) && (ucCount > 0) && stage)
    	{
    	    prevActiveFrameRate = stage.frameRate;
    	    stage.frameRate = _backgroundFrameRate; 
    	}
    }
    


    이는 Flex 4를 이용해 AIR 애플리케이션을 만들때 별도의 조치없이 backgroundFrameRate 속성 설정만으로 frameRate를 조절할 수 있다. 이 값은 기본으로 1이다. 더 좋은 값은 0.01이라고 한다. 이는 거의 멈추게 하는 수준이다. 이 속성은 Flex 4에서 AIR를 위해 WindowedApplication 에만 적용되어 있다. Flash기반인 Application 클래스에는 이 속성이 없다.
    참고글
    Adobe AIR에서 CPU 사용을 줄이는 방법

    글쓴이 : 지돌스타(http://blog.jidolstar.com/624)

      

    일본에 Spark 프로젝트가 있다. 이 프로젝트는 ActionScript Class Library를 오픈소스로 만들고 공유하는 것을 하나의 모토로 삼고 있는데 그 유명한 증강현실(FLARToolKit)을 Flash로 구현한 것도 이 프로젝트에서 진행된 것이다. 이외에도 너무 멋진 라이브러리가 이 프로젝트를 통해 만들어졌고 여기서 만들어진 몇몇 라이브러리를 실무에 적용하고 있다.

     
    Flex 4에는 Spark로 구성된 새로운 컴포넌트들이 존재한다. 그런데 이 Spark가 바로 일본의 Spark 프로젝트의 이름에서 온것임이 확신된다. 즉, Flex 4의 Spark 컴포넌트는 많은 부분에서 일본이 관여했다는 것을 의미한다. 실제로 2009 Adobe MAX에서 Spark 패키지 설계에 나이 20정도 되는 젊은 일본 개발자와 관계가 있다는 것을 확인했다.

    Spark 프로젝트로 일본은 Adobe의 큰 관심을 받게 되었고, 일본 RIA시장에 있어서 없어선 안될 정도로 크게 성장해 있는 상태이다. 아마 일본 RIA 분야에 있어서 자부심을 생기게 하는 훌륭한 프로젝트 팀이라고 생각한다.

    이에 반해 한국은 이 정도로 활발히 진행되는 오픈 프로젝트가 전무하다. 이는 빨리빨리 주위의 한국의 개발 환경덕택에 개발자들이 다른 활동할 수 있는 여지가 없는 것이 가장 큰 문제겠고, 오픈 지향이 미덕으로 방향을 잡고 있는 마당에도 유독 소프트웨어는 폐쇄적인 것도 문제라고 생각한다. 또한 개발자들은 무슨 문서 작성하는 사람처럼 취급해 버리는 무지한 관리자들이 있는한 한국에서 일본과 같은 개방적이고 활발한 개발 커뮤니티나 오픈 소스, 오픈 프로젝트는 어려울 것이다. 당장 문제를 해결하고 이익만 추구하는 한국의 IT시장이 지속된다면 소프트웨어 개발자는 그저 노동만 하고 그에 대한 돈만 벌어가는 존재로 전락할 수 밖에 없다. 창의적인 개발자는 사라지고 그저 일만 받고 처리하는 수동적인 개발자만 늘어나게 될 것이고 결국 한 나라의 IT시장의 발전에 큰 저해요소로 작용할 것이 분명하다.

    한국에도 일본 Spark 프로젝트와 같은 활발하고 멋진 팀이 자발적으로 시행될 수 있는 국가적, 기업적 지원이 적극적으로 이뤄지길 희망한다.

    글쓴이 : 지돌스타(http://blog.jidolstar.com/573)

    후기 : http://blog.jidolstar.com/579

    내일 okgosu.net Flex 세미나가 있습니다.

    자세한 내용은 다음과 같습니다.

     

    ■ 주제:
    1. Flex 활용 UX 구현 테크닉
    2. BlazeDS 활용
    3. Flex 4의 새로운 기능

    ■ 대상 : 웹전산학관련 학과생 및 졸업생, IT분야 종사자, 플렉스 개발자

    ■ 내용
    1. Flex 활용 UX 구현 테크닉
    - UX 개요
    - Flex UX 기획 및 설계
    - UX를 위한 Flex 테크닉
    2. BlazeDS 활용
    - BlazeDS의 개요
    - BlazeDS의 주요 기능
    - LCDS와 BlazeDS의 차이
    - Flex 연동 방법
    3. Flex 4의 새로운 기능
    - Flex 3와의 차이점
    - Flex 4의 새로운 기능
    - Flex의 비전

    ■ 일시 : 2009년 8월 13일(목요일) 19:00~ 22:00

    ■ 장소 :  비트교육센터 지하 2층 멀티미디어관(서울 서초구 서초 2동 1327-15 비트아카데미빌딩)

    주차지원안됨

    지하철 2호선 강남역 3번 출구 삼성화재 건물까지 직진 후,
    오른쪽 골목길로 50M 가량 직진하면 우측에 일식집 “일조수사” 건물

    ■ 형식 : 전문강사의 세미나와 질의 응답

    ■ 시간별 진행일정
    오프닝: 19:00 ~ 19:10
    세션1: 19:10~20:00 Flex를 활용한 UX 구현 테크닉 - 옥상훈
    세션2: 20:10~21:00 BlazeDS 활용 - 배준균
    세션3: 21:10~22:00 Flex 4 새로운 기능 - 지용호

    ■ 강사소개

    옥상훈
    OkGosu.Net 운영자
    한국SW아키텍트 연합회장
    예제로 배우는 플렉스 저자
    전 한국 Adobe 컨설턴트
    배준균
    Adobe Community Champion
    한국 키스코 플렉스 개발팀장
    지용호
    Adobe Community Champion
    flexdocs.kr 운영자
    위콘 커뮤니케이션 UI 개발팀장


    ■ 후원
    OkGosu.Net

     

    접수는 http://www.bitacademy.com/etc/semina_info.asp  에서 하시면 됩니다.

     

    아참~ 참석비용 없습니다.

    Flex 4에 대해서 자료를 모으고 공부하는 중에 매우 괜찮은 블로그를 찾았다.

    http://www.hulstkamp.com/

    이 사람 블로그에 가면 Flex 4기반 커스텀 컴포넌트를 만든 예제들이 정말 많다. 한동안 이 사람 블로그와 친해져야 겠다는 생각이 들었다. ㅎㅎ

    올라온 자료중에 Gumbo(4.0.0.4932) 버전으로 만든 Knob Button 예제가 있었는데 Flex 4 SDK Beta버전이 정식으로 나오기 전이라 바로 실행할 수 없었다. 그래서 소스를 보면서 마이그레이션 작업을 해보았다. 아래 실행 예제는 바로 이 작업의 결과물이다.

    위 프로그램은 Knob Button에 대한 데모이다. 마우스로 돌려볼 수 있다.


    마이그레이션 하면서 Spark 기반 커스텀 컴포넌트를 제작하는 법에 더욱 익숙해질 수 있었다.

    Flex 3의 Halo 기반 컴포넌트는 스킨이 그래픽 기반만 지원했는데 Flex 4의 Spark기반 컴포넌트들은 스킨이 그래픽적 요소 뿐 아니라, 상태변화(state), 다른 컴포넌트 배치, 데이타 표현등이 된다. 또한 FXG를 지원하고 MXML형태로 스킨을 만들 수 있기 때문에 그 확장력이 무궁무진하게 되었다. Halo 컴포넌트의 경우에는 상태변화 바꾸거나 내부 컴포넌트 배치만 달라져도 컴포넌트 자체를 확장해서 다시 만들어야 하는 불편함이 있었지만 Spark컴포넌트는 큰 변경없이 커스텀 스킨만 제작하는 것만으로도 충분히 해결할 수 있게 되었다. 이 내용은 여기에 올려놓은 소스 또는 "Spark DropDownList 사용하기"를 보면 알 수 있을 것이다.

    Spark 컨테이너의 경우에는 Layout까지 동적으로 변경할 수 있도록 만들어져서 언제든지 다른 Layout으로 바꿀 수 있고 또는 커스텀 Layout를 만들어 쓸 수도 있다. 반면 Halo 기반 컨테이너는 컨테이너의 Layout을 바꾸기 위해 기존 컨테이너를 다시 확장하거나 새로 만들어야만 했다. 이 내용에 대해서는 "Spark 컨테이너의 Layout, Scrolling, Viewport 소개"를 읽어보길 바란다.

    CSS는 정말 획기적으로 많이 추가 되었다. 기존 Class, Type Selector밖에 없었는데 Flex 4로 넘어오면서 ID, Descendant, Pseudo, Mutiple Class등 다양한 Selector가 추가되었다. 이에 대한 글은 "Flex 4의 CSS"를 참고하길 바란다.

    아래는 위 프로그램의 소스이다. 참고바란다.


    개발 환경은 Flash Builder 4 Beta 1 이다. Flash Builder는 아래 링크에서 다운 받을 수 있다.

    Flex 4는 Flex 3의 컨테이너의 Scrolling 기능보다 더욱 강화되고 직관적으로 바뀌었다. 여기에 Layout, Viewport라는 개념까지 포함되어 더욱 다루기 편하도록 만들어졌다. 이 글은 Flex 4 Spark 컨테이너의 Layout, Scrolling, Viewport에 대해서 소개한다.

    아래는 Flash Builder 4 환경에서 작업했다. Flash Builder 4는 아래 링크를 통해 다운받을 수 있다.

    http://www.adoberia.co.kr/pds/down.html?src=text&kw=000026  

    Spark 컨테이너의 Layout

     

    Flex 3에서 Halo 기반 컨테이너는 Canvas, Box, HBox, VBox, Tile등이 있었지만 Flex 4의 Spark 기반 컨테이너는 모두 Group 하나로 통일되었고 layout 속성을 통해 BasicLayout, HorizontalLayout, VerticalLayout, TileLayout을 설정하여 Flex 3의 Canvas, HBox, VBox, Tile역할을 할 수 있도록 했다. Flex 4 Spark 기반 컨테이너는 이와 같이 동적으로 Layout을 바꿀 수 있도록 만들어졌기 때문에 좀 더 유연한 컨테이너 환경을 만들 수 있다.

     

    아래는 위 프로그램의 소스이다.

    <?xml version="1.0" encoding="utf-8"?>
    <s:Application 
    	xmlns:fx="http://ns.adobe.com/mxml/2009" 
    	xmlns:s="library://ns.adobe.com/flex/spark" 
    	xmlns:mx="library://ns.adobe.com/flex/halo" 
    	minWidth="300" minHeight="300">
    	<s:layout>
    		<s:VerticalLayout/>
    	</s:layout>
    	<fx:Script>
    		<![CDATA[
    			import spark.layouts.TileLayout;
    			import spark.layouts.VerticalLayout;
    			import spark.layouts.HorizontalLayout;
    		]]>
    	</fx:Script>
    	
    	<s:DropDownList id="ddl" requiresSelection="true" labelField="label">
    		<s:dataProvider>
    			<s:ArrayList>
    				<fx:Object label="TileLayout" layout="{new TileLayout()}"/>
    				<fx:Object label="HorizontalLayout" layout="{new HorizontalLayout()}"/>
    				<fx:Object label="VerticalLayout" layout="{new VerticalLayout()}"/>
    			</s:ArrayList>
    		</s:dataProvider>
    	</s:DropDownList>
    		
    	<s:Group>
    		<s:layout>{ddl.selectedItem.layout}</s:layout>
    		<s:Button label="1"/>
    		<s:Button label="2"/>
    		<s:Button label="3"/>
    		<s:Button label="4"/>
    		<s:Button label="5"/>
    		<s:Button label="6"/>
    		<s:Button label="7"/>
    		<s:Button label="8"/>
    		<s:Button label="9"/>
    		<s:Button label="10"/>
    	</s:Group>
    
    </s:Application>
    
    

     

    Spark 컨테이너의 Scrolling

     

    Flex 3의 모든 컨테이너는 Scroller기능이 내장되어 있었는데 반해 Flex 4 Spark 기반 컨테이너는 기본적으로 Scroller 기능이 없다. 그래서 컨테이너는 더욱 가벼워졌고 필요할 때 ScrollBar를 Group 태그 밖으로 감싸주기만 하는 것으로 스크롤을 추가할 수 있도록 되었다.

     

     

    아래는 앞선 소스에서 Scroller만 추가한 것이다.

    <s:Scroller width="200" height="100" left="1" right="1" top="1" bottom="1">
    	<s:Group>
    		<s:layout>{ddl.selectedItem.layout}</s:layout>
    		<s:Button label="1"/>
    		<s:Button label="2"/>
    		<s:Button label="3"/>
    		<s:Button label="4"/>
    		<s:Button label="5"/>
    		<s:Button label="6"/>
    		<s:Button label="7"/>
    		<s:Button label="8"/>
    		<s:Button label="9"/>
    		<s:Button label="10"/>
    	</s:Group>
    </s:Scroller>
    

     

    Flex 4 Spark 컨테이너(List, Group등)는 이렇게 쉽게 Layout을 임의대로 바꿀 수 있고 Scroller도 쉽게 배치할 수 있도록 되어 있다. Flex 4 Spark 기반 컨테이너는 Flex 3의 Halo 컨테이너보다 더욱 사용하기 간편하고 직관적이며 활용도가 높다는 것을 해본 사람은 알 수 있을 것이다.

     

     

    Spark 컨테이너의 Viewports

     

    Spark 컨테이너에서 들어간 재미있으면서 유용한 개념이 들어갔는데 Viewport 라는 것이다. Viewport는 말그대로 보여지는 영역이다. 아래 그림만 봐도 Viewport의 의미를 쉽게 이해할 수 있을 것이다.

     

    출처 : http://hansmuller-flex.blogspot.com/2009/06/introduction-to-viewports-and-scrolling.html

     

    방금 설명했던 Scroller는 시각적으로 보이는 horizontalScrollbar, verticalScrollbar와 ViewPort의 horizontalScrollPosition, verticalScrollPosition 속성의 값을 묶어(Bind)준다. 그러므로 어느 한쪽이 조절되면 다른 한쪽이 변경되어지게 된다. 백문의 불여일타.... 아래 예제만 봐도 이게 무슨 말인지 바로 알게 된다.

     

     

    <?xml version="1.0" encoding="utf-8"?>
    <!-- 출처 :http://hansmuller-flex.blogspot.com/2009/06/introduction-to-viewports-and-scrolling.html-->
    <s:Application 
        xmlns:fx="http://ns.adobe.com/mxml/2009" 
        xmlns:s="library://ns.adobe.com/flex/spark" 
        xmlns:mx="library://ns.adobe.com/flex/halo">
        <s:Group>
            <s:Group left="20" top="1" width="110" height="160">
                <s:Scroller id="scrollbar" left="2" right="2" top="2" bottom="2">
                    <s:Group id="vp" horizontalScrollPosition="57" verticalScrollPosition="198">
                        <mx:Image source="http://sites.google.com/site/hansmuller/Home/archive/gyro-original.jpg"/>
                    </s:Group>
                </s:Scroller>
                <s:Rect left="0" right="0" top="0" bottom="0">
                    <s:stroke>
                        <s:SolidColorStroke weight="1" color="0xD8D8D8"/>
                    </s:stroke>
                </s:Rect>
            </s:Group>
            <s:SimpleText horizontalCenter="10" top="175" text="viewport.width = {vp.width}"/>
            <s:SimpleText verticalCenter="-10" rotation="-90" text="viewport.height = {vp.height}"/>                
        </s:Group>
    
        <s:VGroup left="140" top="10" gap="15">
            <s:SimpleText text="viewport.contentWidth = {vp.contentWidth}"/>
            <s:SimpleText text="viewport.contentHeight = {vp.contentHeight}"/>
            <s:SimpleText text="viewport.horizontalScrollPosition = {vp.horizontalScrollPosition}"/>
            <s:SimpleText text="viewport.verticalScrollPosition = {vp.verticalScrollPosition}"/>
        </s:VGroup>
    
    </s:Application>
    

     

    Spark 컨테이너인 Group은 GroupBase를 상속받고 GroupBase는 IViewport 인터페이스를 구현하고 있다. Scroller는 이 IViewport과 바인딩되어 있다는 것을 이해하는 것이 중요하다.

     

        public interface IViewport extends IVisualElement
        {
            function get width():Number;
            function get height():Number;
            function get contentWidth():Number;
            function get contentHeight():Number;
            function get horizontalScrollPosition():Number;
            function set horizontalScrollPosition(value:Number):void;
            function get verticalScrollPosition():Number;
            function set verticalScrollPosition(value:Number):void;
            function getHorizontalScrollPositionDelta(scrollUnit:uint):Number;
            function getVerticalScrollPositionDelta(scrollUnit:uint):Number;
            function get clipAndEnableScrolling():Boolean;
            function set clipAndEnableScrolling(value:Boolean):void;
        }
    

     

    그러므로 IViewport 인터페이스를 구현한 GroupBase, Group, DataGroup, RichEditableText, SkinnableContainer, SkinnableDataContainer 등은 모두 Scroller를 사용할 수 있게 되는 것이다.

     

    위 소스에서 사용자가 Scroller를 컨트롤 하지 않고 Viewport를 컨트롤하여 스크롤바를 움직이게 할 수 있다.

     

    위 프로그램은 앞선 소스에서 아래 코드를 추가하면 된다

    <s:VGroup top="196" gap="15" x="20">
    	<s:HGroup>
    		<s:SimpleText text="HScroll Controller"/>    		
    		<s:HSlider id="hSlider" 
    			liveDragging="true"
    			minimum="0" 
    			maximum="{vp.contentWidth-vp.width}" 
    			value="{vp.horizontalScrollPosition}" 
    			change="vp.horizontalScrollPosition=hSlider.value"/>
    	</s:HGroup>
    
    	<s:HGroup>
    		<s:SimpleText text="VScroll Controller"/>    		
    		<s:HSlider id="vSlider" 
    			liveDragging="true"
    			minimum="0" 
    			maximum="{vp.contentHeight-vp.height}" 
    			value="{vp.verticalScrollPosition}" 
    			change="vp.verticalScrollPosition=vSlider.value"/>
    	</s:HGroup>
    </s:VGroup>
    

     

    이 코드가 의미하는 바는 사용자가 ScrollBar를 직접 컨트롤하지 않아도 Viewport를 통해 ScrollBar를 프로그램적으로도 컨트롤 할 수 있다는 것을 보여주고 있다.

     

    (위 프로그램에서 Slider에 Viewport의 위치 값이 초반에 제대로 업데이트 안되는 이유는 이미지 로딩시점과 관련되어 있다. 이미지 로딩 뒤에 Viewport의 위치를 변경하면 아마도 문제가 해결될 것이므로 참고바란다. )

     

     

    참고내용

    Spark Layouts with Flex 4 Beta

    Introduction to Viewports and Scrolling in Gumbo

     

     

    글쓴이 : 지돌스타(http://blog.jidolstar.com/568)

    Flex 4의 Spark 계열 DropDownList는 List를 확장한 컴포넌트이다. Flex 3에서 ComboBox와 유사하게 사용할 수 있다.

     

    DropDownList는 DropDownBase대신 List를 서브클래스로 한 것이 커다란 변화이다. 이렇게 된데에는 Iwo Banas라는 사람이 기존 Gumbo의 FxComboBox을 더욱 확장이 가능하고 내부적으로 독립화된 구조의 필요성에 대한 제안을 반영한 결과이다. 이에 따라 DropDownList는 dropDown 스킨 부분을 더이상 가지지 않고 List의 dataGroup 스킨을 사용하게 되었다.

     

    DropDownList 컴포넌트의 기능 및 디자인 스펙을 보고 싶다면 아래 글을 보기 바란다. 이 글만 잘봐도 DropDownList에 내부구현 및 사용방법에 대해 어느정도 이해할 수 있을 것이라 생각한다.

     

    http://opensource.adobe.com/wiki/display/flexsdk/Spark+DropDownList

     

    또한 API에 대한 자세한 내용은 아래 글을 참고한다.

    http://livedocs.adobe.com/flex/gumbo/langref/spark/components/DropDownList.html

     

    DropDownList를 사용하는 방법은 Peter deHaan의 블로그만 봐도 쉽게 학습할 수 있다.

    나는 이 블로그에서 DropDownList를 언급한 글들만 골라서 사용해 보았다. Flex 3와 달라 약간 생소하지만 놀라운 스킨 적용의 확장성에 매력을 느끼게 될 것이다. 아래에서 소개하는 4개의 큰 제목을 누르면 해당 포스트로 이동한다.

     

    Setting a content background color on a Spark DropDownList control in Flex Gumbo

     

    이 예제는 DropDownList를 이용하는 매우 간단한 예제로 DropDownList의 DropDown되는 리스트의 배경색을 바꾸는 것이다.

     

     

    Setting the symbol color on the Spark DropDownList control in Flex 4

     

     

    이 예제도 그 전 예제와 비슷한 난이도의 예제로 DropDownList의 Symbol의 색을 변경하는 예제이다.

     

     

    Creating a tile layout Spark DropDownList control in Flex Gumbo

     

     

    이 예제는 DropDownList의 커스텀 스킨을 적용하는 방법을 보여주고 있다. 블로그에 있는 커스텀 스킨은 Gumbo버전이라 작동을 잘 안하는데 스킨은 아래 것을 받아 해보길 바란다. Peter deHaan의 예제에서 PopUp을 사용했는데 Flex 4부터는 PopUpAnchor로 바뀌었다.

     

    List에 보여지는 아이템이 원래 DropDownList의 기본 Skin인 spark.skins.DropDownListSkin과 달라진 점은 PopupAnchor의 DataGroup를 VerticalLayout으로 지정하는데 여기서 사용한 Layout은 HorizontalLayout이라는 점이 다르다. 게다가 위치도 아래표시가 아니라 우측에 표시되도록 했다. 이처럼 스킨부분에서 모든 Layout을 수정할 수 있도록 한 것이 Flex 4 컴포넌트의 컨셉이다. 이것은 Flex 3의 단점을 잘 극복한 부분중에 하나이다.

     

    조금 더 재미있게 바꿔보자. Application과 DropDownSkin을 아래 코드로 바꿔보자.

     

     

    스킨만 바꿨을 뿐인데...(갑자기 어느 광고가 생각남.. ㅋ)

    위 프로그램 처럼 DropDownList의 스킨을 바꿀 수 있다. 그것도 아주 쉽게~~

     

     

    Displaying images in a Spark DropDownList control in Flex Gumbo

     

    방금 예제에서 DropDownList의 스킨을 바꾸는 것을 보여주었다. 그럼 DropDownList에 이미지도 보여줄 수 있을까? 당근 아주아주 쉽게 할 수 있다.

     

     

    여기에서 사용된 BitmapImage는  spark.primitives.* 패키지에 포함되는 것으로 Group을 확장한 Graphic내에 넣을 수 있다. Graphic에 넣을 수 있는 것중에는 <Rect>, <Path>, <Ellipse>등도 포함한다. 관련 내용은 아래 링크를 참고한다.

     

    http://livedocs.adobe.com/flex/gumbo/langref/spark/primitives/package-detail.html

     

     

    정리하며

    지금까지 Flex 4의 Spark DropDownList에 대해서 살펴보았다. Flex 3와 달리 Flex 4에서는 스킨 적용시 ActionScript 3.0 기반이 아닌 MXML이라 직관적이고 편리하게 할 수 있다는 것이 큰 매력이였다. 또한 기능도 매우 확대되었다. Spark 컴포넌트라면 이와 같은 방법으로 스킨을 적용할 수 있다는 것을 이해하는 것이 중요하겠다.


    테스트 환경 : Flash Builder 4 + Flex SDK 4

    Flash Builder 는 아래 링크에서 다운 받자.

    http://www.adoberia.co.kr/pds/down.html?src=text&kw=000026 

    글쓴이 : 지돌스타(http://blog.jidolstar.com/567)

    Flex 4로 넘어오면서 생소하게 느꼈던 부분 중에 하나가 바로 자식(Child) 관리 메소드들의 변화가 아닌가 생각한다. 바뀐 부분이 무엇이고 왜 바뀌었는지 알아보도록 하겠다.

     

    Flex 3에서 Canvas, Box 등의 컨테이너는 모두 mx.core.Container 클래스를 확장해서 만들어졌다. 이 Container 클래스는 flash.display.DisplayObjectContainer에서 정의되어 있는 자식관리 메소드인 numChildren, addChild(), addChildAt(), removeChild(), removeChildAt() 등을 그대로 Override(재정의) 해서 사용했다. 그런데 이렇게 재정의 한것이 문제가 컸다. Flex 3의 Container는 DisplayObjectContainer의 메소드를 그대로 사용하므로 자식은 모두 flash.display.DisplayObject로 받게 된다. 하지만 실제 자식으로 사용할 수 있는 것은 IUIComponent를 구현한 클래스만 자식으로 받을 수 있다. 즉 UIComponent만 자식으로 받을 수 있는 것이다. 아래 코드는 Flex 3의 Container에서 자식을 추가할 때 내부적으로 addingChild()메소드가 호출되는데 거기에 있는 코드이다.

    var uiChild:IUIComponent = IUIComponent(child);
    

     

    Flex 3에서 addChild()를 통해 받는 자식은 DisplayObject인데 런타임시 사용될 수 있는 컴포넌트는 IUIComponent이니 실제 Flex 3를 이용해서 개발하는 사람들은 오용할 소지가 크게 된 것이다. 나도 Container에 Sprite 객체를 넣으려고 하다가 안되서 애먹은 적이 있었는데 바로 이것때문이다.

     

    Flex 4부터는 이 문제를 말끔히 없애고자 DisplayObjectContainer의 자식 관리 메소드를 재정의 하지 않고 다른 메소드를 만들었다. 그 다른 메소드는 무엇일까?

     

    Flex 4에서는 아래와 같이 IVisualElementContainer 인터페이스를 정의했다. 이 인터페이스는 Flex 4의 모든 Spark, Halo 컨테이너에서 모두 구현하고 있다. 보면 알겠지만 addChild대신 addElement, removeChild대신 removeElement로 된 것이다. 결국 Child 대신 Element를 사용한거다.

    package mx.core
    {
    public interface IVisualElementContainer
    {
        function get numElements():int;
        function getElementAt(index:int):IVisualElement
        function addElement(element:IVisualElement):IVisualElement;
        function addElementAt(element:IVisualElement, index:int):IVisualElement;
        function removeElement(element:IVisualElement):IVisualElement;
        function removeElementAt(index:int):IVisualElement;
        function removeAllElements():void;
        function getElementIndex(element:IVisualElement):int;
        function setElementIndex(element:IVisualElement, index:int):void;
        function swapElements(element1:IVisualElement, element2:IVisualElement):void;
        function swapElementsAt(index1:int, index2:int):void;
    
    }
    }
    

     

    위 인터페이스는 정확히 무엇을 의미하는가? 자식 추가할 때 이제 더이상 DisplayObject가 아니라는 것에 주목하자. DisplayObject대신 IVisibleElement를 관리하고 있다. 이것이 핵심이다. 즉 이제 런타임시가 아닌 컴파일 단계서부터 원천적으로 컨테이너에 자식들은 모두 IVisibleElement을 구현한 것만 다루겠다는 것이다. Flex 4는 Flex 3와 다르게 모든 컨테이너는 IVisibleElementContainer를 구현한 것이다. 그리고 mx.core.UIComponent는 IVisibleElement를 구현했다. 이로서 컨터이너에 붙는 자식들을 더욱 명확하게 사용할 수 있게 되었다.

     

    그럼 addChild, addChildAt은 어떻게 했을까? 아래 코드는 Spark 컨테이너의 SkinnableComponent와 GroupBase에 재정의된 것이다. 즉 이들 메소드를 사용하면 런타임시 예외처리를 해준다. 이 클래스를 확장해서 만든 Spark 계열의 컨테이너인 SkinnableContainer, SkinnableDataContainer, Group, DataGroup, Panel 전부 이렇게 동작한다.

        /**
         *  @private
         */
        override public function addChild(child:DisplayObject):DisplayObject
        {
            throw(new Error(resourceManager.getString("components", "addChildError")));
        }
        
        /**
         *  @private
         */
        override public function addChildAt(child:DisplayObject, index:int):DisplayObject
        {
            throw(new Error(resourceManager.getString("components", "addChildAtError")));
        }
    

     

    하지만 기존 Halo 기반의 컨테이너(mx.core.Container)는 addChild(), removeChild() 사용시 예외처리를 해두지 않았다. 물론 IVisualElementContainer는 구현했지만 말이다. 이는 기존 Flex 3 Halo 기반의 컴포넌트와의 호환성 문제 때문에 그리 한 것이라 판단한다.

     

    Flex 4 문서를 보면 되도록이면 Halo보다 Spark 기반의 컴포넌트와 컨테이너를 사용할 것을 권장하고 있다. 지금까지 소개한 내용도 그렇게 권장하는 이유중에 하나일 것이다.

     

    Flex SDK 개발자들이 Flex 2 시절 전부터 이런 문제를 알고 있었을 텐데 왜 지금에서야 바꿨는지... ㅎㅎㅎ

     

     

    참고글

    Spark Group - Functional and Design Specification

     

     

    글쓴이 : 지돌스타(http://blog.jidolstar.com/559)

     

     

    InsideRIA에 올라온 Getting started with Spark skins 의 글을 읽고 따라해보았다.

     

    아래는 Skin 소스이다. com.jidolstar.skins.MyButtonSkin으로 이름을 지었다.

    <?xml version="1.0" encoding="utf-8"?>
    <s:Skin 
    	xmlns:fx="http://ns.adobe.com/mxml/2009" 
    	xmlns:s="library://ns.adobe.com/flex/spark" 
    	xmlns:mx="library://ns.adobe.com/flex/halo" 
    	>
    	<fx:Metadata>
    		[HostComponent("spark.components.Button")]
    	</fx:Metadata>
    	
    	<s:states>
    		<mx:State name="up"/>
    		<mx:State name="down"/>
    		<mx:State name="over"/>
    		<mx:State name="disabled"/>
    	</s:states>
    	
    	<s:filters>
    		<s:DropShadowFilter quality="3"
    			alpha="0.5" alpha.over="0.3"
    			distance="5" distance.over="15"
    			blurX.over="15" blurY.over="15"
    			inner.down="true"
    		/>
    	</s:filters>
    	
    	<s:Rect left="0" right="0" top="0" bottom="0" radiusX="5" radiusY="5">
    		<s:fill>
    			<mx:LinearGradient rotation="90">
    				<mx:entries>
    					<mx:GradientEntry color="#0a5c00" ratio="0.00"/>
    					<mx:GradientEntry color="#84a381" ratio="0.50" ratio.over="0.25"/>
    					<mx:GradientEntry color="#0a5c00" ratio="0.80"/>
    				</mx:entries>
    			</mx:LinearGradient>
    		</s:fill>
    	</s:Rect>
    	
    	<s:SimpleText id="labelElement" color="#ffffff"
    		horizontalCenter="0" verticalCenter="0"
         	left="10" right="10" top="5" bottom="5"
         />	
    	
    	<s:layout>
    		<s:BasicLayout/>
    	</s:layout>
    </s:Skin>
    

     

    아래는 위 Skin을 사용한 소스이다.

    <?xml version="1.0" encoding="utf-8"?>
    <s:Application 
    	xmlns:fx="http://ns.adobe.com/mxml/2009" 
    	xmlns:s="library://ns.adobe.com/flex/spark" 
    	xmlns:mx="library://ns.adobe.com/flex/halo" 
    	minWidth="500" minHeight="400"
    	>
    	<s:Button label="확인" skinClass="com.jidolstar.skins.MyButtonSkin"/>
    </s:Application>
    
    

     

    코딩하면서 느끼는 것이지만 Flex 4는 Flex 3하고 너무 다르다. ㅎㅎ  

    스킨 부분에 있어서 Flex 3에서는 ProgrammaticSkin을 기반으로 Skin을 ActionScript 코드로 만들었는데 Flex 4부터는 MXML로 만들 수 있도록 했다. 이 부분이 가장 크게 달라진거다.

     

    위의 Skin 코드를 살펴보면 몇가지 재미있는 점을 발견할 수 있다. 살펴보자.

     

    먼저 HostComponent 메타 데이타를 볼 수 있다. Flex 4에서 나온 새로운 메타 데이타이다. 이것은 이 스킨이 어떤 컴포넌트가 Host인가 명시적으로 지칭해주는 역할을 한다. Button은 ButtonBase를 확장해서 만들었고 ButtonBase는 SkinnableComponent를 확장해서 만들어졌는데 HostComponent가 쓰이는 부분은 SkinnableComponent인 듯 보인다. 아래는 SkinnableComponent내에 정의된 attachSkin() 메소드이다. 이것은 createChildren() 혹은 commitProperties()가 호출될때 이 메소드가 호출되는데 skinClass 스타일이 바뀔 때 Skin을 붙여주는 역할을 담당한다.

     

        /**
         *  Create the skin for the component. 
         *  You do not call this method directly. 
         *  Flex calls it automatically when it calls createChildren() or  
         *  the UIComponent.commitProperties() method.
         *  Typically, a subclass of SkinnableComponent does not override this method.
         * 
         *  

    This method instantiates the skin for the component, * adds the skin as a child of the component, and * resolves all part associations for the skin

    * * @langversion 3.0 * @playerversion Flash 10 * @playerversion AIR 1.5 * @productversion Flex 4 */ protected function attachSkin():void { // Factory var skinClassFactory:IFactory = getStyle("skinFactory") as IFactory; if (skinClassFactory) setSkin( skinClassFactory.newInstance() as Skin ); // Class if (!skin) { var skinClass:Class = getStyle("skinClass") as Class; if (skinClass) setSkin( new skinClass() ); } if (skin) { skin.owner = this; // As a convenience if someone has declared hostComponent // we assign a reference to ourselves. If the hostComponent // property exists as a direct result of utilizing [HostComponent] // metadata it will be strongly typed. We need to do more work // here and only assign if the type exactly matches our component // type. if ("hostComponent" in skin) { try { Object(skin).hostComponent = this; } catch (err:Error) {} } // the skin's styles should be the same as the components skin.styleName = this; super.addChild(skin); skin.addEventListener(PropertyChangeEvent.PROPERTY_CHANGE, skin_propertyChangeHandler); } else { throw(new Error(resourceManager.getString("components", "skinNotFound", [this]))); } findSkinParts(); invalidateSkinState(); }

    위 코드에 보면 hostComponent 부분이 보이는데 바로 HostComponent 메타데이터와 관련이 있어 보인다. 아직 정확한 동작방식은 모르겠다.

     

    주제를 바꿔서 맨 위 코드에 <s:state>가 존재하는 것을 볼 수 있다. 이것은 상태를 의미하는 것으로 이미 Button 자체에 up, down, over, up 상태가 메타 데이터 형태로 정의 되어 있다. . ButtonBase에 보면 아래와 같은 방식으로 4개의 상태를 나타내는 메타 데이터가 정의되어 있다.

    /**
     *  Up State of the Button
     *  
     *  @langversion 3.0
     *  @playerversion Flash 10
     *  @playerversion AIR 1.5
     *  @productversion Flex 4
     */
    [SkinState("up")]
    
    /**
     *  Over State of the Button
     *  
     *  @langversion 3.0
     *  @playerversion Flash 10
     *  @playerversion AIR 1.5
     *  @productversion Flex 4
     */
    [SkinState("over")]
    
    /**
     *  Down State of the Button
     *  
     *  @langversion 3.0
     *  @playerversion Flash 10
     *  @playerversion AIR 1.5
     *  @productversion Flex 4
     */
    [SkinState("down")]
    
    /**
     *  Disabled State of the Button
     *  
     *  @langversion 3.0
     *  @playerversion Flash 10
     *  @playerversion AIR 1.5
     *  @productversion Flex 4
     */
    [SkinState("disabled")]
    
    public class ButtonBase extends SkinnableComponent implements IFocusManagerComponent
    {
       ...
    }
    

     

    저 상태변화는 SkinnableComponent 클래스에서 알아서 처리하도록 되어 있다. Button의 스킨의 상태에 따른 변화의 동작방식을 이해하기 위해 SkinnableComponent를 분석해야한다. 뭐, 분석까지 아니더라도 SkinnableComponent를 활용하는 방법만 알면 될 것이다. SkinnableComponent는 UIComponent를 확장한 것이고 모든 Spark 계열의 컴포넌트는 SkinnableComponent를 확장한 형태이다. ColorPicker나 DateChooser와 같이 예전 Halo 계열의 컴포넌트는 SkinnableComponent를 확장하지 않았다.

     

    <s:filters>는 말그대로 필터를 설정하는 것을 의미한다. 여기서는 DropShadowFilter를 사용했다. Flex 4에서 추가한 새로운 기능중에 각 속성에 state를 지정할 수 있다. DropShadowFilter를 사용할 때도 아래 처럼 속성에 이 기능을 적용했다.  

     

    alpha="0.5" alpha.over="0.3"

     

    DropShadowFilter를 사용한 부분 중에 alpha값을 위처럼 사용했다. 보통때는 alpha값이 0.5이지만 상태가 over이면 0.3을 사용하겠다는 것을 의미한다. alpha.over="0.3" 부분이 어떻게 ActionScript 로 바뀌는가 살펴보려면 컴파일 옵션에 -keep-generated-actionscript를 해보면 알 수 있다. 그럼 컴파일을 하면 src/generated 폴더가 생성된다. 그 안에 com.jidolstar.skins 패키지로 가보면 MyButtonSkin-generated.as 파일이 있는데 열어보면 alpha.over="0.3" 가 어떻게 바뀌는가 한눈에 알 수 있다. 아래 코드는 MyButtonSkin-generated.as의 일부분이다.

     

        /**
         * @private
         **/
        public function MyButtonSkin()
        {
            super();
            // our style settings
    
            // properties
            this.filters = [_MyButtonSkin_DropShadowFilter1_i()];
            this.layout = _MyButtonSkin_BasicLayout1_c();
            this.mxmlContent = [_MyButtonSkin_Rect1_c(), _MyButtonSkin_SimpleText1_i()];
            this.currentState = "up";
    
    
            // events
    		states = [
    		  new State ({
    		    name: "up",
    		    overrides: [
    		    ]
    		  })
    		  ,
    		  new State ({
    		    name: "down",
    		    overrides: [
    		      new mx.states.SetProperty().initializeFromObject({
    		        target: "_MyButtonSkin_DropShadowFilter1",
    		        name: "inner",
    		        value: true
    		      })
    		    ]
    		  })
    		  ,
    		  new State ({
    		    name: "over",
    		    overrides: [
    		      new mx.states.SetProperty().initializeFromObject({
    		        target: "_MyButtonSkin_DropShadowFilter1",
    		        name: "alpha",
    		        value: 0.3
    		      })
    		      ,
    		      new mx.states.SetProperty().initializeFromObject({
    		        target: "_MyButtonSkin_DropShadowFilter1",
    		        name: "distance",
    		        value: 15
    		      })
    		      ,
    		      new mx.states.SetProperty().initializeFromObject({
    		        target: "_MyButtonSkin_DropShadowFilter1",
    		        name: "blurX",
    		        value: 15
    		      })
    		      ,
    		      new mx.states.SetProperty().initializeFromObject({
    		        target: "_MyButtonSkin_DropShadowFilter1",
    		        name: "blurY",
    		        value: 15
    		      })
    		      ,
    		      new mx.states.SetProperty().initializeFromObject({
    		        target: "_MyButtonSkin_GradientEntry2",
    		        name: "ratio",
    		        value: 0.25
    		      })
    		    ]
    		  })
    		  ,
    		  new State ({
    		    name: "disabled",
    		    overrides: [
    		    ]
    		  })
    		];
        }
    
    private function _MyButtonSkin_DropShadowFilter1_i() : spark.filters.DropShadowFilter
    {
    	var temp : spark.filters.DropShadowFilter = new spark.filters.DropShadowFilter();
    	temp.quality = 3;
    	temp.alpha = 0.5;
    	temp.distance = 5;
    	_MyButtonSkin_DropShadowFilter1 = temp;
    	return temp;
    }
    

     

    결국 alpha.over="0.3"는 State 객체를 만들어 각 상태에 따른 속성으로 지정해버리도록 만들어진다. currentState를 "up"으로 설정하고 상태가 바뀔 때마다 등록된 State를 실행하도록 되는 것이다. 이걸 보니깐 원리가 확실히 이해가 된다.

     

    다시 돌아가서....

    <s:Rect>과 <s:SimpleText>는 mxmlContent=[_MyButtonSkin_Rect1_c(), _MyButtonSkin_SimpleText1_i()] 의 형태로 추가 된다. mxmlContent도 Flex 4부터 추가된 속성인데 생소하기 짝이 없다. ㅎㅎ 좀 알아보기 위해 또 뒤져봤다.

     

    //----------------------------------
    //  mxmlContent
    //----------------------------------
    
    private var mxmlContentChanged:Boolean = false;
    private var _mxmlContent:Array;
    
    [ArrayElementType("mx.core.IVisualElement")]
    
    /**
     *  The visual content children for this Group.
     *
     *  

    The content items should only be IVisualItem objectss. * An mxmlContent Array should not be shared between multiple * Group containers because visual elements can only live in one container * at a time.

    * *

    If the content is an Array, do not modify the Array * directly. Use the methods defined by Group class instead.

    * * @default null * * @langversion 3.0 * @playerversion Flash 10 * @playerversion AIR 1.5 * @productversion Flex 4 */ public function get mxmlContent():Array { if (_mxmlContent) return _mxmlContent.concat(); else return null; } /** * @private */ public function set mxmlContent(value:Array):void { if (createChildrenCalled) { setMXMLContent(value); } else { mxmlContentChanged = true; _mxmlContent = value; // we will validate this in createChildren(); } } /** * @private * Adds the elements in mxmlContent to the Group. * Flex calls this method automatically; you do not call it directly. * * @langversion 3.0 * @playerversion Flash 10 * @playerversion AIR 1.5 * @productversion Flex 4 */ private function setMXMLContent(value:Array):void { var i:int; // if there's old content and it's different than what // we're trying to set it to, then let's remove all the old // elements first. if (_mxmlContent != null && _mxmlContent != value) { for (i = _mxmlContent.length - 1; i >= 0; i--) { elementRemoved(_mxmlContent[i], i); } } _mxmlContent = (value) ? value.concat() : null; // defensive copy if (_mxmlContent != null) { var n:int = _mxmlContent.length; for (i = 0; i < n; i++) { var elt:IVisualElement = _mxmlContent[i]; // A common mistake is to bind the viewport property of an Scroller // to a group that was defined in the MXML file with a different parent if (elt.parent && (elt.parent != this)) throw new Error(resourceManager.getString("components", "mxmlElementNoMultipleParents", [elt])); elementAdded(elt, i); } } }

     

    위 코드는 Group 클래스이다. mxmlContent는 Skin 클래스의 부모 클래스인 Group에 구현되어 있는 속성인 것이다. mxmlContent에 값을 설정하면 setMXMLContent() 함수가 호출되어 기존의 MXML 추가된 Element들을 지우고 새로운 Element인 Rect과 SimpleText를 추가한다. 또한 IVisibelElement인 컴포넌트만 자식으로 추가하도록 되어 있고 다른 부모를 가지는 컴포넌트인 경우에 예외처리도 한다. 결국 mxmlContent은 Group에서 사용된 MXML형태의 비주얼 컴포넌트를 Flex 컴파일러가 AS3로 추가해주기 위해 만들어진 것이다.

     

    <s:Application> 부분에 버튼 추가한 부분은 어떻게 변해 있을까? 살펴보면 아래 처럼 되어 있다.

    private function _sparkskin_Button1_c() : spark.components.Button
    {
    	var temp : spark.components.Button = new spark.components.Button();
    	temp.label = "확인";
    	temp.setStyle("skinClass", com.jidolstar.skins.MyButtonSkin);
    	if (!temp.document) temp.document = this;
    	return temp;
    }
    
    

    skinClass가 원래 Style속성이다. SkinnableComponent를 보면 [Style(name="skinClass", type="Class")]로 정의 되어 있다. 이는 validateSkinChange() 함수에서 붙여주게 된다.

     

    /**
     *  @private
     */
    private function validateSkinChange():void
    {
        // If our new skin Class happens to match our existing skin Class there is no
        // reason to fully unload then reload our skin.  
        var skipReload:Boolean = false;
        
        if (_skin)
        {
            var factory:Object = getStyle("skinFactory");
            var newSkinClass:Class;
            
            if (factory)
                newSkinClass = (factory is ClassFactory) ? ClassFactory(factory).generator : null;
            else
                newSkinClass = getStyle("skinClass");
                
            skipReload = newSkinClass && 
                getQualifiedClassName(newSkinClass) == getQualifiedClassName(_skin);
        }
        
        if (!skipReload)
        {
            if (skin)
                detachSkin();
            attachSkin();
        }
    }
    

     

    내가 한가지 흥미를 느꼈던 것은 바로 <s:SimpleText> 부분이였다. Flex 4의 Button은 Label의 컴포넌트를 바꿀 수 없었는데 Flex 4에서는 스킨 기능을 이용해 Text엔진을 바꿀 수 있다. 가령 <s:SimpleText> 대신 <s:RichText>로 바꿔보길 바란다. 잘 된다. 이는 TextGraphicElement를 확장한 SimpleText와 RichText만 가능하다. 만약 TextArea와 같은 것을 사용하면 아래와 같은 에러가 나오게 된다.

     

    TypeError: Error #1034: 유형 강제 변환에 실패했습니다. spark.components::TextArea@70c36d1을(를) spark.primitives.supportClasses.TextGraphicElement(으)로 변환할 수 없습니다.

     

    이런 식의 사용은 Spark 컴포넌트 전체에서 사용할 수 있다. 참 좋은 방법이다.

     

     

    생각하기

     

    Flex 4는 Flex 3 처럼 무리하게 ActionScript 3.0까지 분석할 필요까지 느끼게 해주지 않도록 하는 것이 컨셉인 것 같다. 하지만 내가 좀 독특한 건가 예전 버릇을 버리지 못해 어찌 돌아가는지 알고 싶어서 Flex 4 SDK를 훑어보게 되었다. 하지만 이렇게 저렇게 분석하면서 살펴봤지만 아직도 모르는게 산적해있다. 먼저 사용하는 법부터 좀 공부해야겠다. ㅎㅎ

     

    관련글

    Getting started with Spark skins

    Creating Spark Skins

    The Spark Group and Spark SkinnableContainer containers

     

     

    글쓴이 : 지돌스타(http://blog.jidolstar.com/558)

     

     

    일전에 Flex 4 CSS에 대한 글을 올렸습니다. Flex 4의 CSS는 Flex 3까지의 CSS의 한계를 극복하도록 만들어졌죠. 그런데 이 CSS가 기능이 확장된 계기가 있었습니다. 갑자기 궁금해지지 않나요? Flex 개발에 관련된 Adobe 개발자들이 머리를 짜서 하자고 했을까요? 아니면 어떤 요청이 있었을까요? 답은 Adobe Bug reporting 시스템에 있었습니다.

     

    Adobe에서는 Adobe Bug Reporting 시스템을 운영하고 있습니다. 들어가보면 알겠지만 Flex, BlazeDS, Flash Player, ActionScript Compiler 주제로 개설되어 있고 개발자들의 요구사항 및 버그를 이곳에서 전부 받고 같은 요구사항이 있는 사람들이 투표하는 방식으로 진행되어 투표수가 많으면 Adobe에서 우선순위를 가려서 개발착수에 들어가지요.

    Adobe Bug 리포팅 시스템 첫화면

     

     

    Flex 4의 고급 CSS도 이런 과정을 통해 탄생된 겁니다.

     

    https://bugs.adobe.com/jira/browse/SDK-14385

     

    Flex CSS 지원해달라는 요구사항 페이지

     

    Jacob Wright라는 사람이 리포팅을 했고 투표수가 75입니다. 그만큼 CSS의 기능이 더욱 확장되었으면 좋겠다는 사람의 수가 많은 거지요. 투표수가 많으니 Adobe 측에서도 무시할 수 없게 된 것이고 이번 Flex 4에서 지원하게 된 것입니다.

     

     

     

    버그 리포팅에 참여하자.

     

    그럼 우리도 참여할 수 있을까요?

    때론 그럴겁니다 영어 못하기 때문에 참여 못한다고....

    하지만 꼭 그렇지 않습니다. 자주 들어가 내용을 살펴보고 투표로써 작은 관심을 가져주는 것만으로도 충분합니다. 개발자중에서도 영어 잘하는 사람이 이런 글을 올립니다. 그럼 그 사람의 요청이 있으면 가서 투표해주면 되는 겁니다. 절대 어려운 일이 아닙니다. 아래에 버그 리포팅에서 추천(투표)의 중요성에 대해 알 수 있을 겁니다.

     

    [열이아빠]플렉스 버그 리포팅에서 추천의 중요성

     

    Adobe Flex/AIR 관련되어 한글문제가 꽤 있습니다. 이 한글문제를 해결하기 위해서도 이 버그 리포트 시스템을 이용하면 됩니다. 이미 Flash Platform 한글문제 공동대응팀이 생겼고 지속적으로 이 문제를 해결하기 위해 다양한 각도로 일하고 있습니다. 대응팀 총괄을 맡고 있는 이희덕씨 블로그에 관련 글이 많습니다.

     

    [희희덕덕]한글문제 이슈 관련글

     

    아래글은 열이아빠님이 쓰신 플렉스 버그 검색하는 방법입니다..

     

    [열이아빠]플렉스 버그 검색해보기

     

    투표에 참여해서 Flex/AIR/Flash 한글문제를 해결하는 방법입니다.

     

    [희희덕덕]여러분의 참여로 한글 문제를 함께 해결해 봅시다!

     

     

    앞으로 Flash Platform 한글문제 공동대응팀은 관련 자료를 종합하고 관리할 것입니다.

     

     

    정리하며

     

    여러분도 Adobe RIA 기술에 대한 불만 또는 원하는 사항들이 있을겁니다. 앞에서 설명드린데로 잘 정리해서 Adobe Bug Reporting 시스템에 올리시거나 투표를 적극적으로 참여함으로써 성취할 수 있는 겁니다. 한국 개발자들은 유독 영어 울렁증이 있다고들 호소하는데(저를 비롯) 사실 핑계라고 생각합니다. 필요하면 공부하면 되고요. 이런 일들은 꼭 영어를 잘해서 하는 일도 아니거든요. 한국 Adobe RIA 기술에 관련된 시장은 일본에 비해 거의 8배 정도 부족합니다.  그리고 적극성도 일본에 비해 많이 뒤쳐지며 나오는 컨텐츠의 창의력도 훨씬 뒤지는 편입니다. 왜 유독 일본어만 Flex Livedocs가 있을까요? Adobe는 시장성이 없는 한국에 한글문서 작성 인력을 투자하기에는 그들도 아깝다는 생각을 하는겁니다. 물론 Adobe는 그런 마음을 가지면 안되는 것이지만 또한 적극적이지 못한 우리도 반성해야될 일이라고 생각합니다. 우리가 안으로만 숨지않고 적극적으로 활동하면 Adobe에서 한국시장을 무시 못하게 될겁니다. 그런 날이 오길 반드시 바랍니다.

     

    글쓴이 : 지돌스타(http://blog.jidolstar.com/549)

    Flex 4는 Flex 3를 완전히 갈아 엎었다는 느낌이 든다. 기존 컴포넌트에서 없어진 것도 있고 새로 추가 된 것도 상당히 많다. 익숙해지려면 적지 않은 시간투자가 필요할 것 같다.

     

    CSS에 네임스페이스 추가

     

    Flex 4부터 CSS에 네임스페이스가 추가가 되었다. 이러한 구분이 필요한 것은 컴포넌트가 기존 halo 컴포넌트 외에 Spark 컴포넌트가 새로 추가가 되면서 필요해진 것이다. 즉 두개의 컴퍼넌트가 다른 네임스페이스를 사용하고 있기 때문에 CSS도 다른 네임스페이스를 쓰게 하는 것이다. 다음 소스를 보면 이해가 쉽겠다.

     

    <?xml version="1.0" encoding="utf-8"?>
    <s:Application 
    	xmlns:fx="http://ns.adobe.com/mxml/2009" 
    	xmlns:s="library://ns.adobe.com/flex/spark" 
    	xmlns:mx="library://ns.adobe.com/flex/halo"
    	minWidth="1024" minHeight="768">
        <mx:DateChooser id="main_calendar" x="20" y="20"/>
        <s:Button label="submit" x="220" y="20"/>
    	<fx:Style>
    	 
    	    @namespace s "library://ns.adobe.com/flex/spark";
    	    @namespace mx "library://ns.adobe.com/flex/halo";
    	 
    	    s|Button {
    	        color: #FF0000;
    	    }
    	 
    	    mx|DateChooser {
    	        color: #0000FF;
    	    }
    	 
    	</fx:Style>    
    </s:Application>

     

     

    <fx:Style>내에 @namespace를 정의하고 네임스페이스명|컴포넌트 의 형태로 CSS를 정의할 수 있도록 되었다. 그다지 어려운 사실은 아니다. ^^

     

    이런 구조로 만들어진데에는 Flex 4가 Flash Catalyst와 연동이 되면서 디자이너와 개발자의 협업에 충실해지기 위한 거라고 한다. 예를 들어 Slider의 경우 liveDragging속성이 CSS쪽에서는 제어할 수 없던 반면 이번 Flex 4부터는 할 수 있게 되어 개발자는 코드상에서 디자이너는 CSS쪽에서 접근이 가능해져 협업이 되는 것이다. 디자이너의 경우 CSS를 직접 건드리지 않고 Flash Catalyst를 통해서 작업하기 때문에 CSS에 대해서는 몰라도 되지만 그 결과물은 CSS에 속성이 정의 되어 있음을 확인할 수 있을 것이다. 이러한 점이 개발자와 디자이너간에 협업을 가능하게 한 것이라고 생각한다.

     

     

    고급 CSS (Advanced CSS)

     

    Flex 3의 CSS에서 Selector는 단지 2개 였다. Class SelectorsType Selectors가 그것이다. 하지만 Flex 4부터는 이 Selector 종류가 크게 늘어났다. 이는 더욱 세밀하게 CSS 작업을 할 수 있다는 것을 의미한다. 이제 거의 HTML CSS와 맞먹을 정도인 것 같다. ^^ (CSS는 개인적으로 Flex의 강점중에 하나라고 생각한다. 기존 Flex 3 만으로도 강력한 CSS기능은 나로 하여금 Flex 매력에 푹 빠지게 했다.)

     

    참고로 Class Selector와 Type Selector는 다음과 같다.

     

    Type Selector

     

    같은 종류의 컴포넌트에 적용된다. 다음과 같은 경우 spark.components.Button 및 이를 확장한 컴포넌트에 적용된다.

     

      s|Button { color: #CC0000; }

     

    Class Selector

     

    컴포넌트의 styleName을 지정한 컴포넌트라면 종류에 상관없이 적용이 된다.

     

      .header { background-color: #CCCCCC; }

     

     

    아래부터는 Flex 4에서 추가된 CSS selector에 대해 예제와 함께 살펴본다.

     

    1. ID Selectors

    예전부터 컴포넌트 ID별로 CSS를 줄 수 있으면 좋겠다는 생각을 가졌다. Flex 4에 부터 적용되니 기쁘다. ID별로 CSS를 적용할 수 있기 때문에 같은 계열의 컴포넌트라도 하나의 객체로 생성된 유일한 1개의 컴포넌트에만 CSS를 적용할 수 있게 된다.

    <?xml version="1.0" encoding="utf-8"?>
    <s:Application 
    	xmlns:fx="http://ns.adobe.com/mxml/2009" 
    	xmlns:s="library://ns.adobe.com/flex/spark" 
    	xmlns:mx="library://ns.adobe.com/flex/halo"
    	minWidth="1024" minHeight="768">
        <s:Button id="btnTest" x="41" y="24" label="ID 적용됨"/>
        <s:Button x="128" y="24" label="ID 적용 안됨" />
    	<fx:Style>
    		#btnTest{
    			color: #0000ff;
    		} 
    	</fx:Style>    
    </s:Application>
    

     

    ID가 btnTest로 지정된 것만 Label색이 파란색으로 지정된다. ID를 이용하기 때문에 네임스페이스 구분이 필요없다. 이제 컴포넌트 끼리 CSS 사생활 침해를 금지 시킬 수 있겠구나~ (농담)

     

     

    2. Multiple class selectors

     

    이는 Class selector가 확장된 개념이다. Class Selector는 중첩해서 사용할 수 있다. 아래 예제를 보면 쉽게 이해할 수 있을 것이다.

    <?xml version="1.0" encoding="utf-8"?>
    <s:Application 
    	xmlns:fx="http://ns.adobe.com/mxml/2009" 
    	xmlns:s="library://ns.adobe.com/flex/spark" 
    	xmlns:mx="library://ns.adobe.com/flex/halo"
    	minWidth="1024" minHeight="768">
    	<mx:Label styleName="class1 class2" text="중첩 Class Selector 사용" x="8" y="40"/>
    	<fx:Style>
    		.class1 
    		{
    			color : #ff0000;
    		}
    		
    		.class2
    		{
    			fontFamily : "궁서";
    		}
    	</fx:Style>    
    </s:Application>
    
    

    위 예제에서 Label에 styleName을 설정할 때 class selector를 2개 썼다. class1, class2는 각각 폰트색과 종류를 설정했는데 이 2개의 selector를 사용한 Label은 두개의 class selector에 정의가 적용된다. 결국 Label의 글자색은 빨강, 폰트는 궁서로 보이게 된다.

     

    이렇게 class selector를 중첩이 가능하게 됨에 따라 중복되는 selector가 없도록 깔끔하게 CSS구성을 할 수 있게 되었다.

     

     

    3. Descendant Selectors

    이 selector는 자식/부모 관계에 있는 컴포넌트에 해당한다. Panel위에 Button을 놓았다고 하자. 이때 관계에서 Panel이 부모, Button은 자식이 된다. 특정 부모는 자식에게 어떤 것을 바랄 수 있게 된다. 그래서 사과를 줄수도 있고 바나나도 줄 수 있다. 여기에 특정 부모라는 말이 중요하다. 아무 부모나 자식에게 그렇게 주는 것을 금지한다. 이 관계를 이용한 것이 바로 Descendant selector이다.

     

    <?xml version="1.0" encoding="utf-8"?>
    <s:Application 
    	xmlns:fx="http://ns.adobe.com/mxml/2009" 
    	xmlns:s="library://ns.adobe.com/flex/spark" 
    	xmlns:mx="library://ns.adobe.com/flex/halo"
    	minWidth="1024" minHeight="768">
        <s:Panel x="14" y="42" width="250" height="200">
            <s:Button x="12" y="12" label="Button"/>
        </s:Panel>
        <s:Group x="33" y="393" width="200" height="200">
            <s:Button x="33" y="29" label="Button"/>
        </s:Group>
    	<fx:Style>
    	    @namespace s "library://ns.adobe.com/flex/spark";
    	    @namespace mx "library://ns.adobe.com/flex/halo";
    		s|Panel s|Button{
    			color: #304F6B;
    			baseColor: #5387B7;
    		} 
    	</fx:Style>    
    </s:Application>
    

     

    위 코드에서 볼 수 있듯이 Panel에 자식 Button만 CSS를 적용하도록 되어 있기 때문에 Group위에 올라간 Button에는 CSS적용이 안된다.

     

     

    4. Pseudo selectors

     

    Flex 4에서 상태변화에도 CSS를 적용할 수 있게 되었다. Flex 4에서는 Skin을 ProgrammaticSkin 기반이 아닌 SparkSkin기반으로 MXML형태로 제작할 수 있도록 되어  있다. flex4.swc에 spark.skins.*쪽을 훑어보기 바란다. Button의 경우 ButtonSkin.mxml이 존재하는데 아래와 같이 상태별로 다른 스킨을 줄 수 있도록 되어 있다.

    <s:states>
    	<s:State name="up" />
    	<s:State name="over" />
    	<s:State name="down" />
    	<s:State name="disabled" />
    </s:states>
    

     

    이 상태에 따라서 CSS를 적용할 수 있도록 한것이 바로 Pseudo selector이다. 아래 예제에서 볼 수 있듯이 :를 이용해 컴포넌트의 상태에 따라 CSS를 주는 것을 확인할 수 있다.

    <?xml version="1.0" encoding="utf-8"?>
    <s:Application 
    	xmlns:fx="http://ns.adobe.com/mxml/2009" 
    	xmlns:s="library://ns.adobe.com/flex/spark" 
    	xmlns:mx="library://ns.adobe.com/flex/halo"
    	minWidth="1024" minHeight="768">
    	<s:Button label="Test"/>
    	<fx:Style>
    	    @namespace s "library://ns.adobe.com/flex/spark";
    	    @namespace mx "library://ns.adobe.com/flex/halo";
    	 
    		s|Button:up{
    			color: #304F6B;
    			baseColor: #5387B7;
    		} 
    		
    		s|Button:down 
    		{
    			baseColor: #917541;
    			color: #B8A553;
    		}
    		
    		s|Button:over 
    		{
    			baseColor: #B8A553;
    			color: #6B5630;
    		}
    	</fx:Style>    
    </s:Application>
    
    

     

     

    CSS Selector 사용하기

     

    위에서 설명한 Selector 들은 독립적으로 사용하는 것외에도 다른 Selector와 함께 사용할 수 있다.

     

    Flex 3에서는 아래와 같이 단순한 Type Selector와 범용적으로 사용되는 Class Selector만 사용할 수 있었다.

     

    Button { color:#00FF00; }     /* Simple type selector */
    .special { color:#FF0000; }     /* Universal class selector */

    하지만 Flex 4부터는 새로운 Selector와 함께 섞어서 사용할 수 있게 되었다. 아래 CSS 적용 예시를 보자.

     

    Button { color:#00FF00; }     /* 단순 type selector */
    .special { color:#FF0000; }     /* 범용 class selector */
    Button.special { color:#FF0000; }     /* class selector와 함께 사용된 Type selector  */
    Button#b13 { color:#0000FF; }     /* ID selector와 함께 사용된 Type selector */
    #b13 { color:#FF9933; }     /* 범용 id selector */
    Panel VBox Button { color:#990000; }     /* Descendant selector */
    Button:up { color:#FF9900; }     /* pseudo selector와 함께 사용된 Type selector  */
    :up { color:#FF9933; }     /* 범용 pseudo selector */

     

    위 내용을 좀더 새부적으로 이해하기 위해 예를 하나 들어보겠다.

     

    Panel Button { color:#DD0000; }

     

    위처럼 CSS Selector를 중첩한 경우 경우 다음과 같은 상황에 있는 컴포넌트에 color가 설정된다.

     

    - Panel 위에 Button

    - Panel을 확장한 컴포넌트 위에 Button

    - Panel또는 Panel을 확장한 컴포넌트 위에 Button을 확장한 CheckBox와 같은 커스텀 Button

     

     

    정리하며

     

    Flex 4 CSS의 Selector를 외우기 쉽게 하기 위해 아래를 꼭 기억하자.

    • local name : type selector - 컴포넌트 종류
    • styleName : class selector - 컴포넌트 스타일명
    • identity : id selector - 컴포넌트 ID
    • state : pseudo selector - 컴포넌트 상태
    • display list : descendant selector - 컴포넌트 디스플레이 리스트 부모/자식 관계

     

    참고글

     

    글쓴이 : 지돌스타(http://blog.jidolstar.com/548)

    토성(Saturn)

     

    Papervision3D나 Away3D 라이브러리를 이용하지 않고 Flash Player 10 3D API만을 이용해 태양계의 행성인 토성을 그려보았다. 토성을 그리기 위해 먼저 표면부분과 고리부분의 Texture를 구해야한다. 아래 사이트에서 다양한 행성의 Texture 그림을 구할 수 있다.

     

    http://planetpixelemporium.com/saturn.html

     

    토성의 고리 Texture

    토성의 표면 Texture

     

    Texture를 이용해 3D로 만들기 위한 아이디어는 아래 링크를 참고한다.

     

    [Flash 3D]우리 아가 회전시키기 2-Texture를 이용해 여러모양 만들기

    [Flash 3D]우리 아가 회전시키기-UV맵핑,원근투영 이용

     

     

    토성 그리기 : 표면과 고리가 겹치는 문제

     

    지구와 같은 행성과 다르게 토성의 경우에는 고리가 있다. 토성의 표면은 그냥 구형태로 Mesh데이타를 만들어 처리하면 된다. 하지만 고리의 경우는 다른 형태로 Mesh데이타를 만들어야 한다. 설명은 생략하겠다.

     

    아래는 아래 예시의 소스이다.

    invalid-file

    고리와 구가 겹치는 문제가 있는 소스

     

    토성을 모양을 만들기 위해 토성의 표면과 고리에 대한 Mesh데이타를 만들어야 한다. 아래코드는 고리(Ring) 부분의 Mesh데이타를 만드는 부분이다. 표면부분과 다르게 고리 Texture를 반복해서 그리는 데이타를 만든다.

    /**
     * 고리 Mesh 데이터 작성 
     * @param radius 고리 안쪽 반지름  
     * @param thickness 고리 두께
     * @param div 고리 조각수 
     * @return mesh 데이터 
     */
    function createRingMesh( radius:Number, thickness:Number, div:uint ):GraphicsTrianglePath
    {
        var vertices:Vector. = new Vector.( 0, false );
        var indices:Vector. = new Vector.( 0, false );
        var uvtData:Vector. = new Vector.( 0, false );
        var mesh:GraphicsTrianglePath = new GraphicsTrianglePath( vertices, indices, uvtData, TriangleCulling.NONE );
        var s:Number = 0;
        var x1:Number = radius;
        var x2:Number = radius + thickness;
        var y1:Number = 0;
        var y2:Number = 0;
        var z:Number = 0;          
        var cos:Number;
        var sin:Number;    
        var n:Number = 0;
        
        for( var i:uint = 0; i < div; i++ )
        {
            //Vertices
            mesh.vertices.push( 
                x1, y1, z, 
                x2, y2, z 
            );
            s = Math.PI * 2 * (i + 1) / div;
            cos = Math.cos( s );
            sin = Math.sin( s );
            x1 = radius * cos;
            x2 = (radius + thickness) * cos;
            y1 = radius * sin;
            y2 = (radius + thickness) * sin;
            mesh.vertices.push( 
                x1, y1, z, 
                x2, y2, z 
            );
            //UVT
            mesh.uvtData.push( 
                0,1,1, 
                1,1,1, 
                0,0,1, 
                1,0,1 
            );
            
            //Indices
            n = i * 4;
            mesh.indices.push( n, n+1, n+2, n+2, n+1, n+3 );             
        }    
        
        return mesh;
    }
    

     

    Enterfame시에 렌더링을 실시하는데 Utils3D.projectVectors(), graphics.beginBitmapFill(), graphics.drawTriangles() 메소드를 2번씩 호출한다. 즉, 토성의 구와 고리를 그리기 위해 투영, 렌더링을 2번씩 하는 것이다.

    private function onEnterFrame( event:Event ):void
    {
        world.identity(); //단위행렬로 전환 
        world.appendRotation( getTimer() * 0.0027, Vector3D.Z_AXIS );
        world.appendRotation( xRotation, Vector3D.X_AXIS );
        world.appendRotation( yRotation, Vector3D.Y_AXIS );
        world.appendTranslation(0, 0, viewPortZAxis); //이동 
        world.append(projection.toMatrix3D()); //투영 변환 적용 
        
        // mesh 데이터를  투영하여  projected 생성 
        // uvtData도 갱신된다. 갱신되는 데이터는 T값이다. 
        Utils3D.projectVectors( world, saturnFaceMesh.vertices, saturnFaceProjected, saturnFaceMesh.uvtData );
        Utils3D.projectVectors( world, saturnRingMesh.vertices, saturnRingProjected, saturnRingMesh.uvtData );    
        
        viewport.graphics.clear();
        // Triangle 라인을 그림 
        if( visibleTriangle )
        {
            viewport.graphics.lineStyle( 1, 0xff0000, 0.1 );
        }
        
        //Texture 입힌다.
        viewport.graphics.beginBitmapFill( saturnFaceTexture, null, false, true );
        viewport.graphics.drawTriangles( saturnFaceProjected, getSortedIndices(saturnFaceMesh), saturnFaceMesh.uvtData, saturnFaceMesh.culling );                
        viewport.graphics.beginBitmapFill( saturnRingTexture, null, false, true );
        viewport.graphics.drawTriangles( saturnRingProjected, getSortedIndices(saturnRingMesh), saturnRingMesh.uvtData, saturnRingMesh.culling );                
    }
    

     

    언뜻보면 이 코드는 문제가 없어보이지만 실제로 실행해보면 바로 문제가 있다는 것을 확인할 수 있다.

    아래는 이 프로그램을 실행한 것이다.

     

     

    마우스로 돌려보고 화살표키로 확대/축소도 할 수 있다. 또한 Space키 누르면 삼각형 부분이 보인다.

     

    실행한 결과를 보면 문제가 있다. 토성 표면 뒤에 고리가 보인다. 당연히 저렇게 그려지면 안된다. 이 문제는 위의 방법처럼 Texture와 Mesh데이타가 2개여서는 방법이 나오지 않는다. 왜냐하면 graphics.drawTriangles() 함수를 2번 그려주게 되면 먼저 그린 것은 항상 나중에 그린것에 의해 가려지기 때문이다.

     

     

    토성 그리기 : 고리 겹치지 않게 만들기

     

    invalid-file

    표면과 고리가 겹치지 않는 소스

     

    위처럼 고리가 토성표면에 겹치는 문제를 해결하기 위한 방법은 Texture를 하나로 통합하고 Mesh데이타도 하나로 만든다. 그리고 graphics.drawTriangles() 를 한번만 호출하도록 하면 된다.

     

    아래 함수는 고리, 표면 Texture를 1개의 텍스쳐로 만들어준다.

    /**
     * 텍스쳐 생성 
     * @param faceTexture 표면의 BitmapData
     * @param ringTexture 고리의 BitmapData 
     * @return TexturInfo의 객체 
     */ 
    function createTexture( faceTexture:BitmapData, ringTexture:BitmapData ):TextureInfo
    {
    	var texture:BitmapData;
    
    	texture = new BitmapData( 
    			Math.max( faceTexture.width, ringTexture.width ), 
    			faceTexture.height + ringTexture.height, 
    			true,
    			0xff000000 
    	);
    
    	faceTexture.lock();
    	ringTexture.lock();
    
    	var facePixels:ByteArray ;
    	var ringPixels:ByteArray;
    	facePixels = faceTexture.getPixels( new Rectangle( 0, 0, faceTexture.width, faceTexture.height ) );
    	ringPixels = ringTexture.getPixels( new Rectangle( 0, 0, ringTexture.width, ringTexture.height ) );
    	facePixels.position = 0;
    	ringPixels.position = 0;
    
    	texture.setPixels( 
    		new Rectangle( 0, 0, faceTexture.width, faceTexture.height ), 
    		facePixels 
    	);
    	texture.setPixels( 
    		new Rectangle( 0, faceTexture.height, ringTexture.width, ringTexture.height ), 
    		ringPixels 
    	);
    	
    	var faceRect:Rectangle = new Rectangle( 0, 0, faceTexture.width, faceTexture.height );
    	var ringRect:Rectangle = new Rectangle( 0, faceTexture.height+1, ringTexture.width, ringTexture.height );
    	
    	faceTexture.unlock();
    	ringTexture.unlock();
    	
    	return new TextureInfo( texture, faceRect, ringRect ); 
    }
    

     

    결국 아래 그림처럼 Texture BitmapData가 만들어진다.

     

    위 함수에 TextureInfo는 실제 texture Bitmap정보와 Texture상의 표면과 고리 부분에 대한 영역정보를 가지고 있다. 이 영역정보를 가지고 Mesh 데이타를 만들도록 한다.

     

    /**
     * 토성모양의 Mesh 데이타 
     * @param textureInfo 텍스쳐 정보 
     * @return Mesh 데이타 
     */ 
    function createMesh( textureInfo:TextureInfo ):GraphicsTrianglePath
    {
    	var width:Number = textureInfo.texture.width;
    	var height:Number =textureInfo.texture.height;
    	var faceRect:Rectangle = textureInfo.faceRect;
    	var ringRect:Rectangle = textureInfo.ringRect;
    
    	var minU:Number;
    	var maxU:Number;
    	var minV:Number;
    	var maxV:Number;
    	
    	//표면 Mesh 데이타 만들기 
    	minU = faceRect.x / width;
    	maxU = (faceRect.x + faceRect.width) / width;
    	minV = faceRect.y / height;
    	maxV = (faceRect.y + faceRect.height) / height;
    	var faceMesh:GraphicsTrianglePath = createSphereMesh( 50, 24, 24, minU, maxU, minV, maxV );
    	
    	//고리 Mesh 데이타 만들기 
    	minU = ringRect.x / width;
    	maxU = (ringRect.x + ringRect.width) / width;
    	minV = ringRect.y / height;
    	maxV = (ringRect.y + ringRect.height) / height;
    	var ringMesh:GraphicsTrianglePath = createRingMesh( 70, 20, 50, minU, maxU, minV, maxV );
    
    	//고리 Mesh 데이타에서 Index 부분 조정 
    	var deltaIndex:uint = faceMesh.vertices.length/3; //Vertex는 x,y,z 3개씩 묶이므로... ^^
    	var length:uint = ringMesh.indices.length;
    	for( var i:int = 0; i < length; i++ )
    	{
    		//아래와 같이 2개의 mesh가 합쳐지면 뒤에 붙는  ring부분의 index값이 변경되야한다.
    		ringMesh.indices[i] += deltaIndex; 
    	}
    	
    	//최종 Mesh 데이타 완성 
    	var mesh:GraphicsTrianglePath = new GraphicsTrianglePath();
    	mesh.vertices = faceMesh.vertices.concat( ringMesh.vertices );
    	mesh.uvtData = faceMesh.uvtData.concat( ringMesh.uvtData );
    	mesh.indices = faceMesh.indices.concat( ringMesh.indices );
    	mesh.culling = TriangleCulling.NONE;
    	return mesh;	
    }
    

     

    위 함수는 2개의 Mesh데이터를 이용해 1개의 Mesh데이타로 통합해준다. 이로써 이제는 투영, 렌더링을 한번만 하도록 한다.

     

    private function onEnterFrame( event:Event ):void
    {
    	world.identity(); //단위행렬로 전환 
    	world.appendRotation( getTimer() * 0.0027, Vector3D.Z_AXIS );
    	world.appendRotation( xRotation, Vector3D.X_AXIS );
    	world.appendRotation( yRotation, Vector3D.Y_AXIS );
    	world.appendTranslation(0, 0, viewPortZAxis); //이동 
    	world.append(projection.toMatrix3D()); //투영 변환 적용 
    	
    	// mesh 데이터를  투영하여  projected 생성 
    	// uvtData도 갱신된다. 갱신되는 데이터는 T값이다. 
    	Utils3D.projectVectors( world, mesh.vertices, projected, mesh.uvtData );
    	
    	viewport.graphics.clear();
    
    	// Triangle 라인을 그림 
    	if( visibleTriangle )
    	{
    		viewport.graphics.lineStyle( 1, 0xff0000, 0.1 );
    	}
    	
    	//Texture 입힌다.
    	viewport.graphics.beginBitmapFill( textureInfo.texture, null, false, true );
    	viewport.graphics.drawTriangles( projected, getSortedIndices(mesh), mesh.uvtData, mesh.culling );            	
    
    }
    

     

    위 함수처럼 1번만 투영/렌더링 처리하면 이제 토성의 표면과 고리는 아래처럼 자연스럽게 나오게 된다.

     

     

    하지만 완벽하지 않다. 이유는 렌더링시 Culling을 None으로 지정했기 때문이다. 이는 보이지 않는 표면까지 그린다는 의미이다. 고리의 경우 양쪽면이 다보여야한다. 그러므로 고리의 Mesh데이타는 None으로 지정한다. 하지만 토성 표면의 경우는 화면 앞쪽을 향하는 부분만 보이면 되므로 Culling을 Positive로 지정하면 된다. 첫번째 예제에서는 어짜피 Mesh 데이터가 2개이므로 상관없었지만 두번째의 경우에는 양면 다보여야하는 고리때문에 토성의 표면도 None처리 했다. 이는 보이지 않는 부분까지 렌더링하므로 그만큼 속도저하가 일어난다. 이를 해결하기 위해 Positive로 지정하되 고리를 앞뒤면으로 겹쳐서 그리면 된다. 하지만 이 방법밖에 없는지 의문이다.

     

     

    정리하기

     

    이런 작업은 신나는 일이다. ^^

     

    이 토성에 Light효과를 주어 Shading 예제도 만들어봐야 겠다.

     

    참고로 모든 작업은 Flash Builder 4에서 했으며 Flex 4 SDK를 이용했다. 결과물은 Flash Player 10이상 설치된 브라우져에서만 확인할 수 있다.

     

    위 코드는 아래 링크에서도 볼 수 있다.

     

    Saturn 3D, 토성, 고리 중첩 해결못한 것

    Saturn 3D, 토성, 고리 중첩 해결한 것

     

    참고사이트

    [Flash 3D]우리 아가 회전시키기 2-Texture를 이용해 여러모양 만들기

    [Flash 3D]우리 아가 회전시키기-UV맵핑,원근투영 이용

     

    글쓴이 : 지돌스타(http://blog.jidolstar.com/547)

     

     

     

    Flex SDK 4를 이용한 예제가 Tour de Flex에 추가 되었습니다.

     

    Flex 4가 기존 Flex 3에 비해서 바뀐 부분이 있는데 이를 쉽게 공부하길 원한다면 Tour de Flex를 이용하는 것이 좋을 것 같습니다.

     

    Tour de Flex에는 Flex 4의 Components, Effects, Layouts, Primitives, Styles 사용법에 대해서 쉬운 예제를 제공하고 있습니다.

     

    Flex 개발자라면 꼭 한번 보시길 바랍니다.

     

     

    Tour de Flex는 Web용, 데스크탑용(AIR로 제작됨)이 있습니다.  아래 링크를 통해 보시거나 다운로드 받아 설치하세요.

     

     

     

    Tour de Flex Eclipse Plug-in 소개 및 설치, 사용해보기

    Tour de Flex에서는 Eclipse Plug-in을 제공하고 있습니다. 여러분은 쉽게 Tour de Flex에서 제공하는 200여가지의 Flex 샘플을 Flex Builder 3.x 또는 Eclipse 3.4를 통해 쉽게 검색해 볼 수 있습니다. 다음과 같은 순서로 설치해보십시오.

    1. Flex Builder 3에서 Help > Software Updates > Find and Install 을 선택합니다.
    2. 새창이 뜨면 Search for new features to install 을 선택후 Next 버튼을 누릅니다.
    3. Net Remote Site 버튼을 눌러 새창이 뜨면 Name에 Tour de Flex, URL에 http://tourdeflex.adobe.com/eclipse 를 입력합니다.
    4. Site to include in search 리스트 창에 Tour de Flex만 check하고 Finish 버튼을 누릅니다.
    5. 성공적으로 연결이 되면 Select the features to install 리스트 창에 Tour de Flex가 뜹니다. 이것을 선택하고 Next를 누릅니다.
    6. Feature License에서 라이센스 동의를 선택후 Next 를 누릅니다.
    7. Finish를 눌러 Plug-in을 다운로드 합니다.
    8. 다운로드가 완료되면 install이나 Install All을 눌러 설치합니다.
    9. 설치 완료 이후 자동으로 다시 Flex Builder 3를 구동해야 한다는 메시지가 뜨면 Yes버튼을 눌러 재시작합니다.

     

    설치가 완료되고 Flex Builder 3가 다시 구동되면 메뉴에서 Windows > Other Views에 들어가 봅니다.

     

     

    위 그림처럼 Tour de Flex가 설치되어 있고 이것을 선택하고 OK 버튼을 누릅니다.

     

    아래와 같은 Updating 메시지가 뜨면 Download now를 눌러 설치하세요. 이런 메시지가 뜨는 것으로 보아 Plug-in이 Tour de Flex AIR 프로그램을 자동으로 설치 및 업데이트를 해주는 것으로 보입니다.

     

     

    Flex Builder에 아래와 같은 창이 나오면 Serach Components를 통해 해당 컴포넌트를 검색할 수 있습니다. 필자는 아래 화면과 같이 Button으로 검색해봤습니다. 결과가 나오면 보고 싶은 항목을 선택하여 더블클릭하도록 합니다. 그러면 설치형 Tour de Flex를 실행시켜 해당 컴포넌트의 사용 예제를 보여주도록 되어 있습니다.

     

     

    아래는 Button Loader Skin을 더블클릭으로 선택해서 나온 결과입니다.

     

     

    아쉽게도 약간의 버그가 있는 것 같고 Flash Builder 4에는 아직 제대로 동작이 안되는 것을 확인했습니다. 이 부분에 대해서는 업데이트 될 때까지 기다려야할 것 같습니다. 개발 하면서 궁금한 사항이 있다면 이 Tour de Flex Eclipse Plug-in을 적극 활용하면 도움이 될 것이라 생각합니다.

    Adobe Flash Builder 4 Beta가 배포되었습니다. 공식판은 아니고요. Flash Builder 4는 Flex Builder 3의 차기버전으로 Flex SDK 4(Flex Gumbo)를 기본 SDK로 설정되어 있습니다. Flex SDK 4로 만들어진 애플리케이션은 기본적으로 Flash Player 10 버전에서 동작이 가능합니다. 물론 기존 Flex SDK 3도 사용할 수 있습니다.

     

    아래 링크에서 언제든지 다운로드 받을 수 있습니다.(회원가입하셔야 합니다.) 윈도우, 맥 버전이 있으니 자신의 운영체제에 맞게 다운로드 받으시면 됩니다.

     

    http://www.adobe.com/cfusion/entitlement/index.cfm?e=labs%5Fflashbuilder4

     

     

    다음 글을 참고하세요.

     

    1. 설치하기

    위 링크에서 다운로드 받아 쉽게 설치할 수 있습니다.

     

    처음 설치화면입니다.

     

    C:/Program Files/Adobe/Flash Builder Beta 경로에 기본 설치되는군요.

     

    설치중인 화면입니다.

     

    이름도 Gumbo입니다. ㅎㅎ 아직 Beta라는 것이겠죠.

     

    실행해 보겠습니다.

     

    30일 Trial버전으로 사용해봅니다.

     

    첫 실행 화면입니다. FB 문자가 선명하네요. FB는 Flex Builder가 아니라 Flash Builder 입니다.

     

     

     

    2. 사용해보기

     

    메뉴 구성 변화

    FXP 임포트도 되고 Test Case Class, Test Suit Class도 만들 수 있도록 되어 있네요. Flex Builder로 테스트 주도형 제작이 가능해지겠군요.

     

    Data 메뉴가 상당히 직관적으로 바뀌었네요. 또한 더욱 많은 서비스도 지원하도록 만들어졌고요. 진정한 RIA를 구축하기 위한 서버측 기술 지원을 배려한 기능이군요.

     

     

    이전에 없었던 Data/Services, ASDoc, Network Monitor 등이 지원되네요.

     

    아래는 네트워크 모니터툴입니다. RPC 통신등을 할 때 서버와 통신이 어떻게 되는지 확인하는데 요긴하게 사용할 수 있을 것 같습니다. 이제 Http Watcher가 필요 없겠군요!!!!!

     

     

    Flex 코딩해보기

    Flex Project를 만들어보겠습니다. SDK를 Flex SDK 3.4와 Flex 4.0을 사용할 수 있도록 되어 있네요. Flex 4.0 기반에서 작업해 보겠습니다.

     

    FDT(Flash Development Tools)에서 보이는 구조와 비슷하네요. 프로젝트에 사용하고 있는 Flex 4.0 SDK 라이브러리들도 보이고 각각의 (+)버튼을 누르면 정의된 클래스도 한눈에 볼 수 있습니다. 이거 정말 편해졌군요. SWC도 못봤던 netmone.swc, sparksins.swc등이 있습니다.

     

    재미있는 것은 Flex Gumbo가 Fx이니셜로 컴포넌트가 만들어진 것으로 알고 있었는데 FxApplication이 아닌 그냥 Application입니다.

    그리고 몇가지 namespace(fx, s, mx)로 구분 되었네요. ComboBox가 DropDownList로 이름이 바뀐 것 같습니다.

     

    <?xml version="1.0" encoding="utf-8"?>
    <s:Application
        xmlns:fx="http://ns.adobe.com/mxml/2009"
        xmlns:s="library://ns.adobe.com/flex/spark"
        xmlns:mx="library://ns.adobe.com/flex/halo"
        minWidth="1024" minHeight="768">
        <s:Panel x="14" y="44" width="250" height="200" title="타이틀입니다.">
            <s:Button x="15" y="12" label="버튼"/>
            <s:CheckBox x="15" y="46" label="체크박스"/>
            <mx:ColorPicker x="16" y="71"/>
            <s:DropDownList x="103" y="13"/>
            <s:RichEditableText x="92" y="50" width="147" height="39" text="RichEditableText 컴포넌트"/>
        </s:Panel>
    </s:Application>

     

    디자인 모드입니다. 스킨도 바뀌고 새로운 컴포넌트도 있네요.

     

    실행해볼까요?

    헛! 깔끔해진 실행화면!, Flex가 이젠 기본스킨도 이쁘게 나왔네요.

     

     

    Theme 선택 기능

    프로젝트의 Properties를 보면 Flex Theme가 추가되었습니다. 와우! 그런데 아직 기능이 완벽하지 못한 것 같습니다. 적용이 잘 안되네요 ㅡㅡ;

     

     

    Flash CS4 Component 연동

    디자인 모드에서 Components를 보시면 Custom에 New Flash Componet, New Flash Container가 있습니다. 즉 Flash CS4에서 만들어진 SWC와 연동이 가능해졌다는 것을 의미합니다.

     

    드래그 해서 디자인 뷰에 붙이면 아래처럼 화면에 보이고 선택하면 오른쪽 Properties에 Create in Adobe Flash 버튼이 보입니다.

     

     

    버튼을 누르면 Class를 선택할 수 있게 하고 SWC 파일을 선택합니다.

    Create 버튼을 누르면 Flash CS4가 실행되면서 컴포넌트를 만들 수 있게 되어 있네요.

     

     

    ASDoc 기능

    와우! Panel에 마우스를 올려보니 간단한 설명도 나오네요. Eclipse에서는 되던건데… ㅎㅎ

     

    앗… ASDoc View가 바로 이런 역할을 가지는 거였군요. 해당 컴포넌트를 선택하면 ASDoc View에 아래처럼 보입니다. 강력하군요.

     

     

    Getter/Setter 생성기능 / ASDoc Commnet 기능

     

    엄청난 기능(?)이 추가되었군요. Eclipse에서는 당연히 되던건데 왜 Flex Builder에서는 안되나 궁금했던 기능이 이제야 추가되었군요.

     

    properties를 만들고 해당 속성의 Getter/Setter 기능을 아래처럼 추가할 수 있습니다. 단축키는 없네요. ㅡㅡ;

    또한 Add ASDoc Commnent 기능도 추가 되었습니다. @author, @since 등을 자동으로 붙여줄 수 있으면 좋으련만… 아마 설정이 가능할겁니다. ^^

     

     

    이벤트 핸들러 및 Service Call 등록 기능

     

    코딩작업 없이 Event 핸들러 및 Service Call 기능을 추가할 수 있게 되었습니다.

     

     

    위 그림에서 처럼 버튼을 선택하고 Properties에서 On click에 Generate Event Handler 를 선택하면 아래처럼 이벤트 핸들러가 생성됩니다. 넘 좋군요. ^^

     

    버튼을 click하면 바로 아래 코드가 실행되는겁니다.

     

    그리고 Generate Service Call을 누르면 Data/Services를 생성해서 연결할 수 있습니다. 와우 RIA 개발툴 다운 면모가 보이는군요.

     

     

    File Templates 기능

    메뉴의 Windows의 Perferences에 들어가면 아래와 같은 창이 뜹니다.

    여기에서 Flash Builder > File Template 가 있습니다. 이를 선택하면 자신의 코딩방식을 설정할 수 있도록 되어 있습니다. 좋은 기능이네요.

     

     

    3. 정리하며

    이외에도 아직 제가 발견하지 못한 기능이 상당합니다.

     

    이제 FDT를 사용하지 않아도 Flash Builder로 충분히 예전에 지원되지 않았던 기능들을 사용할 수 있게 되었습니다. 몇가지 버그가 있는 것 같지만 그 버그만 잘 잡아준다면 훌륭합니다.

     

    제가 적지 않은 기능이 있다면 소개해주세요. 그리고 제 블로그에 트랙백(엮인글) 적극 환영합니다. ^^

    Flex, Flash, ActionScript, AIR....

    Adobe RIA 기술을 처음 접해본 이들에게 이 용어들의 구분은 매우 생소하다. 앞에 3개는 모두 Flash Player에서 돌아가는 SWF파일을 만들어주는 툴 및 언어이다. AIR는 이러한 것을 확장해서 데스크탑 영역까지 애플리케이션을 제작할 수 있는 기술이라고 할 수 있다.

     

    많이 헷갈리는 것중에 하나가 바로 Flex, Flash, ActionScript 의 구분이다. ActionScript는 Flex, Flash 애플리케이션을 개발하는 가장 핵심이면서 기반이 되는 언어이다. Flex는 ActionScript 3.0을 확장해 RIA를 지원하기 위한 일종의 프레임워크이다. Flash는 ActionScript 초기 시절부터 있는 일종의 툴로서 이펙트 및 다양한 효과를 제작하는데 적절하도록 구성된다. 결국 Flex, Flash의 태생은 ActionScript 이고 어떤 목적을 가지고 제작하느냐에 따라 Flex, Flash로 구분해서 개발한다. 물론 어떤 것을 선택하든지 결과물은 SWF이다.

     

    재미있는 사실은 Flash는 뭔가 디자인 중심적으로 느껴지는 반면, Flex는 개발적이고 기술적으로 여겨는 풍토가 있다. Flash 개발자라고 말하는 것보다 Flex 개발자라고 하면 연봉협상에도 유리할지 모른다. 하지만 Flash 개발이든 Flex 개발이든 어떤 애플리케이션을 만드냐에 따라 선택이 달라지는 것이지 개발의 질이 달라지는 것은 절대 아니다. 여기서 선택이라는 것은 가령 이렇다. 기업솔루션을 구축해야하는 상황이면 서버 구성 및 데이타 통신, 데이터 관리, 각종 컴포넌트를 아낌없이 지원해주는 Flex로 개발하는 것이 좋다. 하지만 Google Map과 같이 Flex 컴포넌트를 전혀 사용하지 않는다면 Flash 기반이나 순수 ActionScript 기반으로 개발하는 것이 맞다.

     

    Adobe에서 Flex Builder 3의 다음 버전을 Flex가 아닌 Flash Builder 4로 지정하기로 했다. Flash Builder 4는 현재 Flash CS4와 같은 툴이 아니라 지금의 Flex Builder와 같이 Eclipse기반으로 만들어진 툴이다. 또한 Flex 4 SDK(현 Flex Gumbo)를 기반으로 Flex 개발환경을 지원한다. 결국 그런거다. Adobe RIA기술의 핵심은 Flash다. 그리고 Flex도 결국 Flash다. 다만 Flex SDK를 기반으로 제작하면 Flex SDK로 만들어진 Flash 애플리케이션인 것이다.

     

    차기 버전이 Flex Builder 4 대신 Flash Builder 4로 지정한 것은 여러모로 바람직해 보인다. 일단 같은 SWF 결과물을 만들어 준다는 암시를 지니기 때문에 의미전달이 쉬워진다. 겉모습은 전부 Flash이지만 개발방법은 선택하기 나름인 것이다. Flex SDK를 사용하면 그 사람은 여전히 Flex 개발자인 것이다. 앞으로 디자이너와 개발자간에 협력 도구인 Flash Catalyst는 Flash Builder와 매우 자연스럽게 연동이 된다. 그런데 어떤것은 Flex, 어떤 것은 Flash, 이렇게 되면 뭔가 섞일 수 없는 느낌이 든다.

     

    2009년 6월 쯤에 1차 베타버전이 나오고 4분기면 정식 버전이 출시될 것이다. 아무튼 기존 보다 개발의 편의성을 극대화 시킬 수 있는 Flash Builder 4가 나오길 기대한다.

     

    이것만은 기억하자.

    이제 더 이상 Flex는 Flex Builder를 지칭하지 않는다. Flex는 Flex SDK를 지칭하며 그것을 이용해서 개발하는 툴은 Flash Builder 4인 것이다.

    Flash Builder 4는 다음 링크를 통해 다운받으세요.
    http://www.adoberia.co.kr/pds/down.html?src=text&kw=000026 

    + Recent posts